diff --git a/.github/ISSUE_TEMPLATE/00-mobile-issues.md b/.github/ISSUE_TEMPLATE/00-mobile-issues.md
deleted file mode 100644
index f171e80b8b..0000000000
--- a/.github/ISSUE_TEMPLATE/00-mobile-issues.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: Mobile Report
-about: ⚠ Due to current development priorities we are not accepting mobile reports at this time (unless you're willing to fix them yourself!)
----
-
-⚠ **PLEASE READ** ⚠: Due to prioritising finishing the client for desktop first we are not accepting reports related to mobile platforms for the time being, unless you're willing to fix them.
-If you'd like to report a problem or suggest a feature and then work on it, feel free to open an issue and highlight that you'd like to address it yourself in the issue body; mobile pull requests are also welcome.
-Otherwise, please check back in the future when the focus of development shifts towards mobile!
diff --git a/.github/ISSUE_TEMPLATE/01-bug-issues.md b/.github/ISSUE_TEMPLATE/01-bug-issues.md
index c8c41e5a78..0b80ce44dd 100644
--- a/.github/ISSUE_TEMPLATE/01-bug-issues.md
+++ b/.github/ISSUE_TEMPLATE/01-bug-issues.md
@@ -8,4 +8,9 @@ about: Issues regarding encountered bugs.
**osu!lazer version:**
-**Logs:**
+**Logs:**
+
diff --git a/.github/ISSUE_TEMPLATE/02-crash-issues.md b/.github/ISSUE_TEMPLATE/02-crash-issues.md
index 8ad27e9e31..ada8de73c0 100644
--- a/.github/ISSUE_TEMPLATE/02-crash-issues.md
+++ b/.github/ISSUE_TEMPLATE/02-crash-issues.md
@@ -8,6 +8,11 @@ about: Issues regarding crashes or permanent freezes.
**osu!lazer version:**
-**Logs:**
+**Logs:**
+
**Computer Specifications:**
diff --git a/README.md b/README.md
index 67027bb9f3..77c7eb9d2d 100644
--- a/README.md
+++ b/README.md
@@ -13,35 +13,36 @@ Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the
## Status
-This project is still heavily under development, but is in a state where users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve over the coming months and hopefully bring some new unique features to the table.
+This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
-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.
+We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
-Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh/home/changelog).
-
-## Requirements
-
-- A desktop platform with the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) or higher installed.
-- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
-- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
-- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
-- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
+- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
+- You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management).
+- Read peppy's [latest blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where lazer is currently and the roadmap going forward.
## Running osu!
-### Releases
-
-If you are not interested in developing the game, you can still consume our [binary releases](https://github.com/ppy/osu/releases).
+If you are looking to install or test osu! without setting up a development environment, you can consume our [binary releases](https://github.com/ppy/osu/releases). Handy links below will download the latest version for your operating system of choice:
**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) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
-| ------------- | ------------- | ------------- | ------------- |
+| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
+| ------------- | ------------- | ------------- | ------------- | ------------- |
-- **Linux** users are recommended to self-compile until we have official deployment in place.
+- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
+## Developing or debugging
+
+Please make sure you have the following prerequisites:
+
+- A desktop platform with the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) or higher installed.
+- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
+- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
+- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
+
### Downloading the source code
Clone the repository:
diff --git a/build/InspectCode.cake b/build/InspectCode.cake
index 06c56dce87..2e7a1d1b28 100644
--- a/build/InspectCode.cake
+++ b/build/InspectCode.cake
@@ -1,5 +1,5 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.33"
-#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.3.0"
+#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.3.2"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
diff --git a/osu.Android.props b/osu.Android.props
index 25bde037db..1c4a6ffe75 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -25,7 +25,6 @@
portable
False
DEBUG;TRACE
- false
false
true
false
@@ -34,7 +33,6 @@
false
None
True
- true
false
False
true
@@ -53,7 +51,7 @@
-
-
+
+
diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj
index ac3905a372..0598a50530 100644
--- a/osu.Android/osu.Android.csproj
+++ b/osu.Android/osu.Android.csproj
@@ -13,6 +13,7 @@
osu.Android
Properties\AndroidManifest.xml
armeabi-v7a;x86;arm64-v8a
+ false
cjk;mideast;other;rare;west
@@ -52,4 +53,4 @@
-
\ No newline at end of file
+
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index b9294088f4..c34e1e1221 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -29,7 +29,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-apple-overlay@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-apple-overlay@2x.png
new file mode 100644
index 0000000000..4233d9bb6e
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-apple-overlay@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-apple@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-apple@2x.png
new file mode 100644
index 0000000000..043bfbfae1
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-apple@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-bananas-overlay@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-bananas-overlay@2x.png
new file mode 100644
index 0000000000..4233d9bb6e
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-bananas-overlay@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-bananas@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-bananas@2x.png
new file mode 100644
index 0000000000..043bfbfae1
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-bananas@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-catcher-idle@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-catcher-idle@2x.png
new file mode 100644
index 0000000000..76949ccfcc
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-catcher-idle@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-drop@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-drop@2x.png
new file mode 100644
index 0000000000..ec2fdbdbdb
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-drop@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-grapes-overlay@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-grapes-overlay@2x.png
new file mode 100644
index 0000000000..4233d9bb6e
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-grapes-overlay@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-grapes@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-grapes@2x.png
new file mode 100644
index 0000000000..043bfbfae1
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-grapes@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-orange-overlay@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-orange-overlay@2x.png
new file mode 100644
index 0000000000..4233d9bb6e
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-orange-overlay@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-orange@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-orange@2x.png
new file mode 100644
index 0000000000..043bfbfae1
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-orange@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-pear-overlay@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-pear-overlay@2x.png
new file mode 100644
index 0000000000..4233d9bb6e
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-pear-overlay@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-pear@2x.png b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-pear@2x.png
new file mode 100644
index 0000000000..043bfbfae1
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/metrics-skin/fruit-pear@2x.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-apple-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-apple-overlay.png
new file mode 100644
index 0000000000..8d9608cfc9
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-apple-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-apple.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-apple.png
new file mode 100644
index 0000000000..be1bda0383
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-apple.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-bananas-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-bananas-overlay.png
new file mode 100644
index 0000000000..3a6612378e
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-bananas-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-bananas.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-bananas.png
new file mode 100644
index 0000000000..afb8698b2d
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-bananas.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-drop.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-drop.png
new file mode 100644
index 0000000000..12c74f46e2
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-drop.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-grapes-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-grapes-overlay.png
new file mode 100644
index 0000000000..bb37ba1920
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-grapes-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-grapes.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-grapes.png
new file mode 100644
index 0000000000..10699b1f31
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-grapes.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-orange-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-orange-overlay.png
new file mode 100644
index 0000000000..e86aa6e7e3
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-orange-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-orange.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-orange.png
new file mode 100644
index 0000000000..42cc80399f
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-orange.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-pear-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-pear-overlay.png
new file mode 100644
index 0000000000..5c479da954
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-pear-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-pear.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-pear.png
new file mode 100644
index 0000000000..9fe400bdd1
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-pear.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-plate.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-plate.png
new file mode 100644
index 0000000000..1da1fdde85
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-plate.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-ryuuta.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-ryuuta.png
new file mode 100644
index 0000000000..f732092379
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-ryuuta.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit0.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit0.png
new file mode 100644
index 0000000000..2d312ceefd
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit0.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit100.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit100.png
new file mode 100644
index 0000000000..7884dc072d
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit100.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit300.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit300.png
new file mode 100644
index 0000000000..3e4ec2e047
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit300.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit50.png b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit50.png
new file mode 100644
index 0000000000..f02ad11a17
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit50.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-apple-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-apple-overlay.png
new file mode 100755
index 0000000000..fe567d158d
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-apple-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-apple.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-apple.png
new file mode 100755
index 0000000000..17f3be9c26
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-apple.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-bananas-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-bananas-overlay.png
new file mode 100755
index 0000000000..2c94ea78bf
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-bananas-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-bananas.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-bananas.png
new file mode 100755
index 0000000000..2c94ea78bf
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-bananas.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-fail.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-fail.png
new file mode 100755
index 0000000000..1eea5c2083
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-fail.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-idle.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-idle.png
new file mode 100755
index 0000000000..17177f3246
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-idle.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-kiai.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-kiai.png
new file mode 100755
index 0000000000..31be03b014
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-kiai.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-drop-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-drop-overlay.png
new file mode 100755
index 0000000000..56bf4a92fb
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-drop-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-drop.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-drop.png
new file mode 100755
index 0000000000..f259684055
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-drop.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-grapes-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-grapes-overlay.png
new file mode 100755
index 0000000000..17f3be9c26
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-grapes-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-grapes.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-grapes.png
new file mode 100755
index 0000000000..3dc60464cf
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-grapes.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-orange-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-orange-overlay.png
new file mode 100755
index 0000000000..3dc60464cf
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-orange-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-orange.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-orange.png
new file mode 100755
index 0000000000..3dc60464cf
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-orange.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-pear-overlay.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-pear-overlay.png
new file mode 100755
index 0000000000..3dc60464cf
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-pear-overlay.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-pear.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-pear.png
new file mode 100755
index 0000000000..3dc60464cf
Binary files /dev/null and b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-pear.png differ
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs
index 0ad72412fc..20911b8d06 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Catch.Objects.Drawable;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Tests.Visual;
@@ -29,6 +29,12 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
+ [Test]
+ public void TestBananaShower()
+ {
+ AddUntilStep("player is done", () => !Player.ValidForResume);
+ }
+
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
@@ -40,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Tests
}
};
- beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 5000, NewCombo = true });
+ beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 3000, NewCombo = true });
return beatmap;
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
index 9b529a2e4c..4ff9f7a7fe 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -3,104 +3,31 @@
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 osuTK.Graphics;
-using osu.Framework.Audio.Sample;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics.Textures;
-using osu.Game.Audio;
-using osu.Game.Graphics.Sprites;
+using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
- public class TestSceneCatcher : OsuTestScene
+ public class TestSceneCatcher : SkinnableTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(CatcherSprite),
+ typeof(CatcherArea),
};
- 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", () =>
+ SetContents(() => new CatcherArea.Catcher
{
- container.Child = new CatchCustomSkinSourceContainer
- {
- Child = new CatcherSprite()
- };
+ RelativePositionAxes = Axes.None,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
});
}
-
- private class CatcherCustomSkin : Container
- {
- public CatcherCustomSkin()
- {
- RelativeSizeAxes = Axes.Both;
-
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Blue
- },
- new OsuSpriteText
- {
- Text = "custom"
- }
- };
- }
- }
-
- [Cached(typeof(ISkinSource))]
- private class CatchCustomSkinSourceContainer : Container, ISkinSource
- {
- public event Action SourceChanged
- {
- add { }
- remove { }
- }
-
- public Drawable GetDrawableComponent(ISkinComponent component)
- {
- switch (component.LookupName)
- {
- case "Gameplay/catch/fruit-catcher-idle":
- return new CatcherCustomSkin();
- }
-
- return null;
- }
-
- public SampleChannel GetSample(ISampleInfo sampleInfo) =>
- throw new NotImplementedException();
-
- public Texture GetTexture(string componentName) =>
- throw new NotImplementedException();
-
- public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
- }
}
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
index 3ae6886c31..df1ac4c725 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
@@ -3,20 +3,24 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
+using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
- public class TestSceneCatcherArea : OsuTestScene
+ public class TestSceneCatcherArea : SkinnableTestScene
{
private RulesetInfo catchRuleset;
- private TestCatcherArea catcherArea;
public override IReadOnlyList RequiredTypes => new[]
{
@@ -26,20 +30,26 @@ namespace osu.Game.Rulesets.Catch.Tests
public TestSceneCatcherArea()
{
AddSliderStep("CircleSize", 0, 8, 5, createCatcher);
- AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t));
+ AddToggleStep("Hyperdash", t =>
+ CreatedDrawables.OfType().Select(i => i.Child)
+ .OfType().ForEach(c => c.ToggleHyperDash(t)));
+
+ AddRepeatStep("catch fruit", () =>
+ this.ChildrenOfType().ForEach(area =>
+ area.MovableCatcher.PlaceOnPlate(new DrawableFruit(new TestSceneFruitObjects.TestCatchFruit(FruitVisualRepresentation.Grape)))), 20);
}
private void createCatcher(float size)
{
- Child = new CatchInputManager(catchRuleset)
+ SetContents(() => new CatchInputManager(catchRuleset)
{
RelativeSizeAxes = Axes.Both,
- Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
+ Child = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.TopLeft
},
- };
+ });
}
[BackgroundDependencyLoader]
@@ -55,6 +65,8 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
+ public new Catcher MovableCatcher => base.MovableCatcher;
+
public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperDashState(status ? 2 : 1);
}
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
index 1eb913e900..070847c0c1 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Catch.Objects.Drawable;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs
index 44517382f7..82d5aa936f 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs
@@ -5,78 +5,114 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Catch.Objects.Drawable;
-using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
+using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
- public class TestSceneFruitObjects : OsuTestScene
+ public class TestSceneFruitObjects : SkinnableTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
typeof(CatchHitObject),
typeof(Fruit),
+ typeof(FruitPiece),
typeof(Droplet),
+ typeof(Banana),
+ typeof(BananaShower),
typeof(DrawableCatchHitObject),
typeof(DrawableFruit),
typeof(DrawableDroplet),
- typeof(BananaShower),
+ typeof(DrawableBanana),
+ typeof(DrawableBananaShower),
typeof(Pulp),
};
- public TestSceneFruitObjects()
+ protected override void LoadComplete()
{
- Add(new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- Content = new[]
- {
- new Drawable[]
- {
- createDrawable(0),
- createDrawable(1),
- createDrawable(2),
- },
- new Drawable[]
- {
- createDrawable(3),
- createDrawable(4),
- createDrawable(5),
- },
- }
- });
+ base.LoadComplete();
+
+ foreach (FruitVisualRepresentation rep in Enum.GetValues(typeof(FruitVisualRepresentation)))
+ AddStep($"show {rep}", () => SetContents(() => createDrawable(rep)));
+
+ AddStep("show droplet", () => SetContents(createDrawableDroplet));
+
+ AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet));
+
+ foreach (FruitVisualRepresentation rep in Enum.GetValues(typeof(FruitVisualRepresentation)))
+ AddStep($"show hyperdash {rep}", () => SetContents(() => createDrawable(rep, true)));
}
- private DrawableFruit createDrawable(int index)
+ private Drawable createDrawableTinyDroplet()
{
- Fruit fruit = index == 5
- ? new Banana
- {
- StartTime = 1000000000000,
- IndexInBeatmap = index,
- Scale = 1.5f,
- }
- : new Fruit
- {
- StartTime = 1000000000000,
- IndexInBeatmap = index,
- Scale = 1.5f,
- };
+ var droplet = new TinyDroplet
+ {
+ StartTime = Clock.CurrentTime,
+ Scale = 1.5f,
+ };
- return new DrawableFruit(fruit)
+ return new DrawableTinyDroplet(droplet)
{
Anchor = Anchor.Centre,
- RelativePositionAxes = Axes.Both,
+ RelativePositionAxes = Axes.None,
Position = Vector2.Zero,
Alpha = 1,
LifetimeStart = double.NegativeInfinity,
LifetimeEnd = double.PositiveInfinity,
};
}
+
+ private Drawable createDrawableDroplet()
+ {
+ var droplet = new Droplet
+ {
+ StartTime = Clock.CurrentTime,
+ Scale = 1.5f,
+ };
+
+ return new DrawableDroplet(droplet)
+ {
+ Anchor = Anchor.Centre,
+ RelativePositionAxes = Axes.None,
+ Position = Vector2.Zero,
+ Alpha = 1,
+ LifetimeStart = double.NegativeInfinity,
+ LifetimeEnd = double.PositiveInfinity,
+ };
+ }
+
+ private Drawable createDrawable(FruitVisualRepresentation rep, bool hyperdash = false)
+ {
+ Fruit fruit = new TestCatchFruit(rep)
+ {
+ Scale = 1.5f,
+ HyperDashTarget = hyperdash ? new Banana() : null
+ };
+
+ return new DrawableFruit(fruit)
+ {
+ Anchor = Anchor.Centre,
+ RelativePositionAxes = Axes.None,
+ Position = Vector2.Zero,
+ Alpha = 1,
+ LifetimeStart = double.NegativeInfinity,
+ LifetimeEnd = double.PositiveInfinity,
+ };
+ }
+
+ public class TestCatchFruit : Fruit
+ {
+ public TestCatchFruit(FruitVisualRepresentation rep)
+ {
+ VisualRepresentation = rep;
+ StartTime = 1000000000000;
+ }
+
+ public override FruitVisualRepresentation VisualRepresentation { get; }
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs
new file mode 100644
index 0000000000..cbc87459e1
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs
@@ -0,0 +1,56 @@
+// 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 NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Tests.Visual;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ public class TestSceneJuiceStream : PlayerTestScene
+ {
+ public TestSceneJuiceStream()
+ : base(new CatchRuleset())
+ {
+ }
+
+ [Test]
+ public void TestJuiceStreamEndingCombo()
+ {
+ AddUntilStep("player is done", () => !Player.ValidForResume);
+ }
+
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap
+ {
+ BeatmapInfo = new BeatmapInfo
+ {
+ BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, SliderMultiplier = 2 },
+ Ruleset = ruleset
+ },
+ HitObjects = new List
+ {
+ new JuiceStream
+ {
+ X = 0.5f,
+ Path = new SliderPath(PathType.Linear, new[]
+ {
+ Vector2.Zero,
+ new Vector2(0, 100)
+ }),
+ StartTime = 200
+ },
+ new Banana
+ {
+ X = 0.5f,
+ StartTime = 1000,
+ NewCombo = true
+ }
+ }
+ };
+ }
+}
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 9559d13328..8c371db257 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
index db52fbac1b..1a5d0f983b 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
@@ -34,9 +34,14 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
foreach (var obj in Beatmap.HitObjects.OfType())
{
- obj.IndexInBeatmap = index++;
+ obj.IndexInBeatmap = index;
+ foreach (var nested in obj.NestedHitObjects.OfType())
+ nested.IndexInBeatmap = index;
+
if (obj.LastInCombo && obj.NestedHitObjects.LastOrDefault() is IHasComboInformation lastNested)
lastNested.LastInCombo = true;
+
+ index++;
}
}
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index e5c3647f99..b9d791fdb1 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -21,6 +21,8 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using System;
+using osu.Game.Rulesets.Catch.Skinning;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch
{
@@ -141,6 +143,8 @@ namespace osu.Game.Rulesets.Catch
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
+ public override ISkin CreateLegacySkinProvider(ISkinSource source) => new CatchLegacySkinTransformer(source);
+
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score);
public int LegacyID => 2;
diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs
index 7e482d4045..02c045f363 100644
--- a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs
+++ b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs
@@ -5,5 +5,11 @@ namespace osu.Game.Rulesets.Catch
{
public enum CatchSkinComponents
{
+ FruitBananas,
+ FruitApple,
+ FruitGrapes,
+ FruitOrange,
+ FruitPear,
+ Droplet
}
}
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs
index 8377b3786a..e2465d727e 100644
--- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs
@@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModDifficultyAdjust : ModDifficultyAdjust
{
- [SettingSource("Fruit Size", "Override a beatmap's set CS.")]
+ [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)]
public BindableNumber CircleSize { get; } = new BindableFloat
{
Precision = 0.1f,
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Mods
Value = 5,
};
- [SettingSource("Approach Rate", "Override a beatmap's set AR.")]
+ [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)]
public BindableNumber ApproachRate { get; } = new BindableFloat
{
Precision = 0.1f,
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs
index 606a935229..ee88edbea1 100644
--- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs
@@ -3,7 +3,7 @@
using System.Linq;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.Objects.Drawable;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs
index 0c754412e5..c3488aec11 100644
--- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs
+++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.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 osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Catch.Objects
@@ -11,6 +12,8 @@ namespace osu.Game.Rulesets.Catch.Objects
public override bool LastInCombo => true;
+ public override Judgement CreateJudgement() => new IgnoreJudgement();
+
protected override void CreateNestedHitObjects()
{
base.CreateNestedHitObjects();
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index e4ad49ea50..f3b566f340 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Objects
{
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
{
- public const double OBJECT_RADIUS = 44;
+ public const float OBJECT_RADIUS = 64;
private float x;
@@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Objects
public int IndexInBeatmap { get; set; }
- public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(ComboIndex % 4);
+ public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
public virtual bool NewCombo { get; set; }
@@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Catch.Objects
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
- Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
+ Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
@@ -100,8 +100,8 @@ namespace osu.Game.Rulesets.Catch.Objects
{
Pear,
Grape,
- Raspberry,
Pineapple,
+ Raspberry,
Banana // banananananannaanana
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs
deleted file mode 100644
index 5afdb14888..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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.Rulesets.Catch.Objects.Drawable
-{
- public class DrawableBanana : DrawableFruit
- {
- public DrawableBanana(Banana h)
- : base(h)
- {
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
deleted file mode 100644
index 059310d671..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
+++ /dev/null
@@ -1,33 +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 osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
-using osuTK;
-
-namespace osu.Game.Rulesets.Catch.Objects.Drawable
-{
- public class DrawableDroplet : PalpableCatchHitObject
- {
- private Pulp pulp;
-
- public override bool StaysOnPlate => false;
-
- public DrawableDroplet(Droplet h)
- : base(h)
- {
- Origin = Anchor.Centre;
- Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
- Masking = false;
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- AddInternal(pulp = new Pulp { Size = Size });
-
- AccentColour.BindValueChanged(colour => { pulp.AccentColour = colour.NewValue; }, true);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
deleted file mode 100644
index 53a018c9f4..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
+++ /dev/null
@@ -1,316 +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 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.Utils;
-using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
-using osuTK;
-using osuTK.Graphics;
-
-namespace osu.Game.Rulesets.Catch.Objects.Drawable
-{
- public class DrawableFruit : PalpableCatchHitObject
- {
- private Circle border;
-
- private const float drawable_radius = (float)CatchHitObject.OBJECT_RADIUS * radius_adjust;
-
- ///
- /// Because we're adding a border around the fruit, we need to scale down some.
- ///
- private const float radius_adjust = 1.1f;
-
- public DrawableFruit(Fruit h)
- : base(h)
- {
- Origin = Anchor.Centre;
-
- Size = new Vector2(drawable_radius);
- Masking = false;
-
- Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- // todo: this should come from the skin.
- AccentColour.Value = colourForRepresentation(HitObject.VisualRepresentation);
-
- AddRangeInternal(new[]
- {
- createPulp(HitObject.VisualRepresentation),
- border = new Circle
- {
- EdgeEffect = new EdgeEffectParameters
- {
- Hollow = !HitObject.HyperDash,
- Type = EdgeEffectType.Glow,
- Radius = 4 * radius_adjust,
- Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Value.Darken(1).Opacity(0.6f)
- },
- Size = new Vector2(Height),
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- BorderColour = Color4.White,
- BorderThickness = 3f * radius_adjust,
- Children = new Framework.Graphics.Drawable[]
- {
- new Box
- {
- AlwaysPresent = true,
- Colour = AccentColour.Value,
- Alpha = 0,
- RelativeSizeAxes = Axes.Both
- }
- }
- },
- });
-
- if (HitObject.HyperDash)
- {
- AddInternal(new Pulp
- {
- RelativePositionAxes = Axes.Both,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- AccentColour = Color4.Red,
- Blending = BlendingParameters.Additive,
- Alpha = 0.5f,
- Scale = new Vector2(1.333f)
- });
- }
- }
-
- private Framework.Graphics.Drawable createPulp(FruitVisualRepresentation representation)
- {
- const float large_pulp_3 = 8f * radius_adjust;
- const float distance_from_centre_3 = 0.15f;
-
- const float large_pulp_4 = large_pulp_3 * 0.925f;
- const float distance_from_centre_4 = distance_from_centre_3 / 0.925f;
-
- const float small_pulp = large_pulp_3 / 2;
-
- static Vector2 positionAt(float angle, float distance) => new Vector2(
- distance * MathF.Sin(angle * MathF.PI / 180),
- distance * MathF.Cos(angle * MathF.PI / 180));
-
- switch (representation)
- {
- default:
- return new Container();
-
- case FruitVisualRepresentation.Raspberry:
- return new Container
- {
- RelativeSizeAxes = Axes.Both,
- Children = new Framework.Graphics.Drawable[]
- {
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(small_pulp),
- Y = -0.34f,
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_4),
- Position = positionAt(0, distance_from_centre_4),
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_4),
- Position = positionAt(90, distance_from_centre_4),
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_4),
- Position = positionAt(180, distance_from_centre_4),
- },
- new Pulp
- {
- Size = new Vector2(large_pulp_4),
- AccentColour = AccentColour.Value,
- Position = positionAt(270, distance_from_centre_4),
- },
- }
- };
-
- case FruitVisualRepresentation.Pineapple:
- return new Container
- {
- RelativeSizeAxes = Axes.Both,
- Children = new Framework.Graphics.Drawable[]
- {
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(small_pulp),
- Y = -0.3f,
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_4),
- Position = positionAt(45, distance_from_centre_4),
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_4),
- Position = positionAt(135, distance_from_centre_4),
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_4),
- Position = positionAt(225, distance_from_centre_4),
- },
- new Pulp
- {
- Size = new Vector2(large_pulp_4),
- AccentColour = AccentColour.Value,
- Position = positionAt(315, distance_from_centre_4),
- },
- }
- };
-
- case FruitVisualRepresentation.Pear:
- return new Container
- {
- RelativeSizeAxes = Axes.Both,
- Children = new Framework.Graphics.Drawable[]
- {
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(small_pulp),
- Y = -0.33f,
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_3),
- Position = positionAt(60, distance_from_centre_3),
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_3),
- Position = positionAt(180, distance_from_centre_3),
- },
- new Pulp
- {
- Size = new Vector2(large_pulp_3),
- AccentColour = AccentColour.Value,
- Position = positionAt(300, distance_from_centre_3),
- },
- }
- };
-
- case FruitVisualRepresentation.Grape:
- return new Container
- {
- RelativeSizeAxes = Axes.Both,
- Children = new Framework.Graphics.Drawable[]
- {
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(small_pulp),
- Y = -0.25f,
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_3),
- Position = positionAt(0, distance_from_centre_3),
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_3),
- Position = positionAt(120, distance_from_centre_3),
- },
- new Pulp
- {
- Size = new Vector2(large_pulp_3),
- AccentColour = AccentColour.Value,
- Position = positionAt(240, distance_from_centre_3),
- },
- }
- };
-
- case FruitVisualRepresentation.Banana:
- return new Container
- {
- RelativeSizeAxes = Axes.Both,
- Children = new Framework.Graphics.Drawable[]
- {
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(small_pulp),
- Y = -0.3f
- },
- new Pulp
- {
- AccentColour = AccentColour.Value,
- Size = new Vector2(large_pulp_4 * 0.8f, large_pulp_4 * 2.5f),
- Y = 0.05f,
- },
- }
- };
- }
- }
-
- protected override void Update()
- {
- base.Update();
-
- border.Alpha = (float)Math.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
- }
-
- private Color4 colourForRepresentation(FruitVisualRepresentation representation)
- {
- switch (representation)
- {
- default:
- case FruitVisualRepresentation.Pear:
- return new Color4(17, 136, 170, 255);
-
- case FruitVisualRepresentation.Grape:
- return new Color4(204, 102, 0, 255);
-
- case FruitVisualRepresentation.Raspberry:
- return new Color4(121, 9, 13, 255);
-
- case FruitVisualRepresentation.Pineapple:
- return new Color4(102, 136, 0, 255);
-
- case FruitVisualRepresentation.Banana:
- switch (RNG.Next(0, 3))
- {
- default:
- return new Color4(255, 240, 0, 255);
-
- case 1:
- return new Color4(255, 192, 0, 255);
-
- case 2:
- return new Color4(214, 221, 28, 255);
- }
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/BananaPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/BananaPiece.cs
new file mode 100644
index 0000000000..ebb0bf0f2c
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/BananaPiece.cs
@@ -0,0 +1,31 @@
+// 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.Rulesets.Catch.Objects.Drawables.Pieces;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ public class BananaPiece : PulpFormation
+ {
+ public BananaPiece()
+ {
+ InternalChildren = new Drawable[]
+ {
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(SMALL_PULP),
+ Y = -0.3f
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_4 * 0.8f, LARGE_PULP_4 * 2.5f),
+ Y = 0.05f,
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs
new file mode 100644
index 0000000000..cf7231ebb2
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.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 System.Collections.Generic;
+using osu.Framework.Utils;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ public class DrawableBanana : DrawableFruit
+ {
+ public DrawableBanana(Banana h)
+ : base(h)
+ {
+ }
+
+ private Color4? colour;
+
+ protected override Color4 GetComboColour(IReadOnlyList comboColours)
+ {
+ // override any external colour changes with banananana
+ return colour ??= getBananaColour();
+ }
+
+ private Color4 getBananaColour()
+ {
+ switch (RNG.Next(0, 3))
+ {
+ default:
+ return new Color4(255, 240, 0, 255);
+
+ case 1:
+ return new Color4(255, 192, 0, 255);
+
+ case 2:
+ return new Color4(214, 221, 28, 255);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs
similarity index 97%
rename from osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs
rename to osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs
index ea415e18fa..4ce80aceb8 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
-namespace osu.Game.Rulesets.Catch.Objects.Drawable
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
public class DrawableBananaShower : DrawableCatchHitObject
{
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs
similarity index 70%
rename from osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
rename to osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs
index b7c05392f3..5bfe0515a1 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs
@@ -2,24 +2,51 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osuTK;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
+using osuTK;
+using osuTK.Graphics;
-namespace osu.Game.Rulesets.Catch.Objects.Drawable
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
public abstract class PalpableCatchHitObject : DrawableCatchHitObject
where TObject : CatchHitObject
{
public override bool CanBePlated => true;
+ protected Container ScaleContainer { get; private set; }
+
protected PalpableCatchHitObject(TObject hitObject)
: base(hitObject)
{
- Scale = new Vector2(HitObject.Scale);
+ Origin = Anchor.Centre;
+ Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
+ Masking = false;
}
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ AddRangeInternal(new Drawable[]
+ {
+ ScaleContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ }
+ });
+
+ ScaleContainer.Scale = new Vector2(HitObject.Scale);
+ }
+
+ protected override Color4 GetComboColour(IReadOnlyList comboColours) =>
+ comboColours[(HitObject.IndexInBeatmap + 1) % comboColours.Count];
}
public abstract class DrawableCatchHitObject : DrawableCatchHitObject
@@ -41,6 +68,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
public virtual bool StaysOnPlate => CanBePlated;
+ public float DisplayRadius => DrawSize.X / 2 * Scale.X * HitObject.Scale;
+
protected DrawableCatchHitObject(CatchHitObject hitObject)
: base(hitObject)
{
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs
new file mode 100644
index 0000000000..0a8e830af9
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.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.Framework.Utils;
+using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ public class DrawableDroplet : PalpableCatchHitObject
+ {
+ public override bool StaysOnPlate => false;
+
+ public DrawableDroplet(Droplet h)
+ : base(h)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ ScaleContainer.Child = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.Droplet), _ => new Pulp
+ {
+ Size = Size / 4,
+ AccentColour = { BindTarget = AccentColour }
+ });
+ }
+
+ protected override void UpdateInitialTransforms()
+ {
+ base.UpdateInitialTransforms();
+
+ // roughly matches osu-stable
+ float startRotation = RNG.NextSingle() * 20;
+ double duration = HitObject.TimePreempt + 2000;
+
+ this.RotateTo(startRotation).RotateTo(startRotation + 720, duration);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs
new file mode 100644
index 0000000000..197ad41247
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.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 osu.Framework.Allocation;
+using osu.Framework.Utils;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ public class DrawableFruit : PalpableCatchHitObject
+ {
+ public DrawableFruit(Fruit h)
+ : base(h)
+ {
+ Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ ScaleContainer.Child = new SkinnableDrawable(
+ new CatchSkinComponent(getComponent(HitObject.VisualRepresentation)), _ => new FruitPiece());
+ }
+
+ private CatchSkinComponents getComponent(FruitVisualRepresentation hitObjectVisualRepresentation)
+ {
+ switch (hitObjectVisualRepresentation)
+ {
+ case FruitVisualRepresentation.Pear:
+ return CatchSkinComponents.FruitPear;
+
+ case FruitVisualRepresentation.Grape:
+ return CatchSkinComponents.FruitGrapes;
+
+ case FruitVisualRepresentation.Pineapple:
+ return CatchSkinComponents.FruitApple;
+
+ case FruitVisualRepresentation.Raspberry:
+ return CatchSkinComponents.FruitOrange;
+
+ case FruitVisualRepresentation.Banana:
+ return CatchSkinComponents.FruitBananas;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(hitObjectVisualRepresentation), hitObjectVisualRepresentation, null);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs
similarity index 87%
rename from osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
rename to osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs
index a24821b3ce..932464cfd1 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
-namespace osu.Game.Rulesets.Catch.Objects.Drawable
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
public class DrawableJuiceStream : DrawableCatchHitObject
{
@@ -42,10 +42,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
switch (hitObject)
{
case CatchHitObject catchObject:
- return createDrawableRepresentation?.Invoke(catchObject)?.With(o => ((DrawableCatchHitObject)o).CheckPosition = p => CheckPosition?.Invoke(p) ?? false);
+ return createDrawableRepresentation?.Invoke(catchObject)?.With(o =>
+ ((DrawableCatchHitObject)o).CheckPosition = p => CheckPosition?.Invoke(p) ?? false);
}
- return base.CreateNestedHitObject(hitObject);
+ throw new ArgumentException($"{nameof(hitObject)} must be of type {nameof(CatchHitObject)}.");
}
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableTinyDroplet.cs
similarity index 60%
rename from osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs
rename to osu.Game.Rulesets.Catch/Objects/Drawables/DrawableTinyDroplet.cs
index d41aea1e7b..ae775684d8 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableTinyDroplet.cs
@@ -1,16 +1,21 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osuTK;
+using osu.Framework.Allocation;
-namespace osu.Game.Rulesets.Catch.Objects.Drawable
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
public class DrawableTinyDroplet : DrawableDroplet
{
public DrawableTinyDroplet(TinyDroplet h)
: base(h)
{
- Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 8;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ ScaleContainer.Scale /= 2;
}
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs
new file mode 100644
index 0000000000..5797588ded
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs
@@ -0,0 +1,116 @@
+// 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;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Objects.Drawables;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ internal class FruitPiece : CompositeDrawable
+ {
+ ///
+ /// Because we're adding a border around the fruit, we need to scale down some.
+ ///
+ public const float RADIUS_ADJUST = 1.1f;
+
+ private Circle border;
+
+ private CatchHitObject hitObject;
+
+ private readonly IBindable accentColour = new Bindable();
+
+ public FruitPiece()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(DrawableHitObject drawableObject)
+ {
+ DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject;
+ hitObject = drawableCatchObject.HitObject;
+
+ accentColour.BindTo(drawableCatchObject.AccentColour);
+
+ AddRangeInternal(new[]
+ {
+ getFruitFor(drawableCatchObject.HitObject.VisualRepresentation),
+ border = new Circle
+ {
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ BorderColour = Color4.White,
+ BorderThickness = 6f * RADIUS_ADJUST,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ AlwaysPresent = true,
+ Alpha = 0,
+ RelativeSizeAxes = Axes.Both
+ }
+ }
+ },
+ });
+
+ if (hitObject.HyperDash)
+ {
+ AddInternal(new Circle
+ {
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ BorderColour = Color4.Red,
+ BorderThickness = 12f * RADIUS_ADJUST,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ AlwaysPresent = true,
+ Alpha = 0.3f,
+ Blending = BlendingParameters.Additive,
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Red,
+ }
+ }
+ });
+ }
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+ border.Alpha = (float)Math.Clamp((hitObject.StartTime - Time.Current) / 500, 0, 1);
+ }
+
+ private Drawable getFruitFor(FruitVisualRepresentation representation)
+ {
+ switch (representation)
+ {
+ case FruitVisualRepresentation.Pear:
+ return new PearPiece();
+
+ case FruitVisualRepresentation.Grape:
+ return new GrapePiece();
+
+ case FruitVisualRepresentation.Pineapple:
+ return new PineapplePiece();
+
+ case FruitVisualRepresentation.Banana:
+ return new BananaPiece();
+
+ case FruitVisualRepresentation.Raspberry:
+ return new RaspberryPiece();
+ }
+
+ return Empty();
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/GrapePiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/GrapePiece.cs
new file mode 100644
index 0000000000..1d1faf893b
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/GrapePiece.cs
@@ -0,0 +1,43 @@
+// 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.Rulesets.Catch.Objects.Drawables.Pieces;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ public class GrapePiece : PulpFormation
+ {
+ public GrapePiece()
+ {
+ InternalChildren = new Drawable[]
+ {
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(SMALL_PULP),
+ Y = -0.25f,
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_3),
+ Position = PositionAt(0, DISTANCE_FROM_CENTRE_3),
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_3),
+ Position = PositionAt(120, DISTANCE_FROM_CENTRE_3),
+ },
+ new Pulp
+ {
+ Size = new Vector2(LARGE_PULP_3),
+ AccentColour = { BindTarget = AccentColour },
+ Position = PositionAt(240, DISTANCE_FROM_CENTRE_3),
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/PearPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/PearPiece.cs
new file mode 100644
index 0000000000..7f14217cda
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/PearPiece.cs
@@ -0,0 +1,43 @@
+// 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.Rulesets.Catch.Objects.Drawables.Pieces;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ public class PearPiece : PulpFormation
+ {
+ public PearPiece()
+ {
+ InternalChildren = new Drawable[]
+ {
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(SMALL_PULP),
+ Y = -0.33f,
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_3),
+ Position = PositionAt(60, DISTANCE_FROM_CENTRE_3),
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_3),
+ Position = PositionAt(180, DISTANCE_FROM_CENTRE_3),
+ },
+ new Pulp
+ {
+ Size = new Vector2(LARGE_PULP_3),
+ AccentColour = { BindTarget = AccentColour },
+ Position = PositionAt(300, DISTANCE_FROM_CENTRE_3),
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/Pulp.cs
similarity index 62%
rename from osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
rename to osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/Pulp.cs
index 1e9daf18db..1e7506a257 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/Pulp.cs
@@ -1,16 +1,16 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
+namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
{
- public class Pulp : Circle, IHasAccentColour
+ public class Pulp : Circle
{
public Pulp()
{
@@ -22,32 +22,23 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
Colour = Color4.White.Opacity(0.9f);
}
- private Color4 accentColour;
+ public readonly Bindable AccentColour = new Bindable();
- public Color4 AccentColour
+ protected override void LoadComplete()
{
- get => accentColour;
- set
- {
- accentColour = value;
- if (IsLoaded) updateAccentColour();
- }
+ base.LoadComplete();
+
+ AccentColour.BindValueChanged(updateAccentColour, true);
}
- private void updateAccentColour()
+ private void updateAccentColour(ValueChangedEvent colour)
{
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = Size.X / 2,
- Colour = accentColour.Darken(0.2f).Opacity(0.75f)
+ Colour = colour.NewValue.Darken(0.2f).Opacity(0.75f)
};
}
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- updateAccentColour();
- }
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/PineapplePiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/PineapplePiece.cs
new file mode 100644
index 0000000000..c328ba1837
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/PineapplePiece.cs
@@ -0,0 +1,49 @@
+// 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.Rulesets.Catch.Objects.Drawables.Pieces;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ public class PineapplePiece : PulpFormation
+ {
+ public PineapplePiece()
+ {
+ InternalChildren = new Drawable[]
+ {
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(SMALL_PULP),
+ Y = -0.3f,
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_4),
+ Position = PositionAt(45, DISTANCE_FROM_CENTRE_4),
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_4),
+ Position = PositionAt(135, DISTANCE_FROM_CENTRE_4),
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_4),
+ Position = PositionAt(225, DISTANCE_FROM_CENTRE_4),
+ },
+ new Pulp
+ {
+ Size = new Vector2(LARGE_PULP_4),
+ AccentColour = { BindTarget = AccentColour },
+ Position = PositionAt(315, DISTANCE_FROM_CENTRE_4),
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/PulpFormation.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/PulpFormation.cs
new file mode 100644
index 0000000000..be70c3400c
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/PulpFormation.cs
@@ -0,0 +1,43 @@
+// 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;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Objects.Drawables;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ public abstract class PulpFormation : CompositeDrawable
+ {
+ protected readonly IBindable AccentColour = new Bindable();
+
+ protected const float LARGE_PULP_3 = 16f * FruitPiece.RADIUS_ADJUST;
+ protected const float DISTANCE_FROM_CENTRE_3 = 0.15f;
+
+ protected const float LARGE_PULP_4 = LARGE_PULP_3 * 0.925f;
+ protected const float DISTANCE_FROM_CENTRE_4 = DISTANCE_FROM_CENTRE_3 / 0.925f;
+
+ protected const float SMALL_PULP = LARGE_PULP_3 / 2;
+
+ protected PulpFormation()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ protected static Vector2 PositionAt(float angle, float distance) => new Vector2(
+ distance * MathF.Sin(angle * MathF.PI / 180),
+ distance * MathF.Cos(angle * MathF.PI / 180));
+
+ [BackgroundDependencyLoader]
+ private void load(DrawableHitObject drawableObject)
+ {
+ DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject;
+ AccentColour.BindTo(drawableCatchObject.AccentColour);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/RaspberryPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/RaspberryPiece.cs
new file mode 100644
index 0000000000..22ce3ba5b3
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/RaspberryPiece.cs
@@ -0,0 +1,49 @@
+// 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.Rulesets.Catch.Objects.Drawables.Pieces;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ public class RaspberryPiece : PulpFormation
+ {
+ public RaspberryPiece()
+ {
+ InternalChildren = new Drawable[]
+ {
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(SMALL_PULP),
+ Y = -0.34f,
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_4),
+ Position = PositionAt(0, DISTANCE_FROM_CENTRE_4),
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_4),
+ Position = PositionAt(90, DISTANCE_FROM_CENTRE_4),
+ },
+ new Pulp
+ {
+ AccentColour = { BindTarget = AccentColour },
+ Size = new Vector2(LARGE_PULP_4),
+ Position = PositionAt(180, DISTANCE_FROM_CENTRE_4),
+ },
+ new Pulp
+ {
+ Size = new Vector2(LARGE_PULP_4),
+ AccentColour = { BindTarget = AccentColour },
+ Position = PositionAt(270, DISTANCE_FROM_CENTRE_4),
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index b014b32305..642ff0246e 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -7,6 +7,7 @@ using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@@ -19,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.Objects
///
private const float base_scoring_distance = 100;
+ public override Judgement CreateJudgement() => new IgnoreJudgement();
+
public int RepeatCount { get; set; }
public double Velocity;
@@ -75,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Objects
Samples = tickSamples,
StartTime = t + lastEvent.Value.Time,
X = X + Path.PositionAt(
- lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X / CatchPlayfield.BASE_WIDTH,
+ lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X / CatchPlayfield.BASE_WIDTH,
});
}
}
diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs
new file mode 100644
index 0000000000..36164c5543
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs
@@ -0,0 +1,58 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Humanizer;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Audio;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Skinning
+{
+ public class CatchLegacySkinTransformer : ISkin
+ {
+ private readonly ISkin source;
+
+ public CatchLegacySkinTransformer(ISkinSource source)
+ {
+ this.source = source;
+ }
+
+ public Drawable GetDrawableComponent(ISkinComponent component)
+ {
+ if (!(component is CatchSkinComponent catchSkinComponent))
+ return null;
+
+ switch (catchSkinComponent.Component)
+ {
+ case CatchSkinComponents.FruitApple:
+ case CatchSkinComponents.FruitBananas:
+ case CatchSkinComponents.FruitOrange:
+ case CatchSkinComponents.FruitGrapes:
+ case CatchSkinComponents.FruitPear:
+ var lookupName = catchSkinComponent.Component.ToString().Kebaberize();
+ if (GetTexture(lookupName) != null)
+ return new LegacyFruitPiece(lookupName);
+
+ break;
+
+ case CatchSkinComponents.Droplet:
+ if (GetTexture("fruit-drop") != null)
+ return new LegacyFruitPiece("fruit-drop") { Scale = new Vector2(0.8f) };
+
+ break;
+ }
+
+ return null;
+ }
+
+ public Texture GetTexture(string componentName) => source.GetTexture(componentName);
+
+ public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample);
+
+ public IBindable GetConfig(TLookup lookup) => source.GetConfig(lookup);
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs
new file mode 100644
index 0000000000..25ee0811d0
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Skinning;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Catch.Skinning
+{
+ internal class LegacyFruitPiece : CompositeDrawable
+ {
+ private readonly string lookupName;
+
+ private readonly IBindable accentColour = new Bindable();
+ private Sprite colouredSprite;
+
+ public LegacyFruitPiece(string lookupName)
+ {
+ this.lookupName = lookupName;
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(DrawableHitObject drawableObject, ISkinSource skin)
+ {
+ DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject;
+
+ accentColour.BindTo(drawableCatchObject.AccentColour);
+
+ InternalChildren = new Drawable[]
+ {
+ colouredSprite = new Sprite
+ {
+ Texture = skin.GetTexture(lookupName),
+ Colour = drawableObject.AccentColour.Value,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new Sprite
+ {
+ Texture = skin.GetTexture($"{lookupName}-overlay"),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ };
+
+ if (drawableCatchObject.HitObject.HyperDash)
+ {
+ var hyperDash = new Sprite
+ {
+ Texture = skin.GetTexture(lookupName),
+ Colour = Color4.Red,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Blending = BlendingParameters.Additive,
+ Depth = 1,
+ Alpha = 0.7f,
+ Scale = new Vector2(1.2f)
+ };
+
+ AddInternal(hyperDash);
+ }
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ accentColour.BindValueChanged(colour => colouredSprite.Colour = colour.NewValue, true);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index 2d71fb93fb..2319c5ac1f 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -6,7 +6,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Catch.Objects.Drawable;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index 1de0b6bfa3..b977d46611 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -4,14 +4,16 @@
using System;
using System.Linq;
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.Input.Bindings;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Catch.Objects.Drawable;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
@@ -48,6 +50,9 @@ namespace osu.Game.Rulesets.Catch.UI
public void OnResult(DrawableCatchHitObject fruit, JudgementResult result)
{
+ if (result.Judgement is IgnoreJudgement)
+ return;
+
void runAfterLoaded(Action action)
{
if (lastPlateableFruit == null)
@@ -63,6 +68,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (result.IsHit && fruit.CanBePlated)
{
+ // create a new (cloned) fruit to stay on the plate. the original is faded out immediately.
var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit.HitObject);
if (caughtFruit == null) return;
@@ -73,11 +79,11 @@ namespace osu.Game.Rulesets.Catch.UI
caughtFruit.Anchor = Anchor.TopCentre;
caughtFruit.Origin = Anchor.Centre;
- caughtFruit.Scale *= 0.7f;
+ caughtFruit.Scale *= 0.5f;
caughtFruit.LifetimeStart = caughtFruit.HitObject.StartTime;
caughtFruit.LifetimeEnd = double.MaxValue;
- MovableCatcher.Add(caughtFruit);
+ MovableCatcher.PlaceOnPlate(caughtFruit);
lastPlateableFruit = caughtFruit;
if (!fruit.StaysOnPlate)
@@ -86,7 +92,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (fruit.HitObject.LastInCombo)
{
- if (((CatchJudgement)result.Judgement).ShouldExplodeFor(result))
+ if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result))
runAfterLoaded(() => MovableCatcher.Explode());
else
MovableCatcher.Drop();
@@ -133,7 +139,6 @@ namespace osu.Game.Rulesets.Catch.UI
X = 0.5f;
Origin = Anchor.TopCentre;
- Anchor = Anchor.TopLeft;
Size = new Vector2(CATCHER_SIZE);
if (difficulty != null)
@@ -221,9 +226,9 @@ namespace osu.Game.Rulesets.Catch.UI
/// Add a caught fruit to the catcher's stack.
///
/// The fruit that was caught.
- public void Add(DrawableHitObject fruit)
+ public void PlaceOnPlate(DrawableCatchHitObject fruit)
{
- float ourRadius = fruit.DrawSize.X / 2 * fruit.Scale.X;
+ float ourRadius = fruit.DisplayRadius;
float theirRadius = 0;
const float allowance = 6;
@@ -240,6 +245,12 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.X = Math.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2);
caughtFruit.Add(fruit);
+
+ Add(new HitExplosion(fruit)
+ {
+ X = fruit.X,
+ Scale = new Vector2(fruit.HitObject.Scale)
+ });
}
///
@@ -388,32 +399,24 @@ namespace osu.Game.Rulesets.Catch.UI
}
}
+ public void UpdatePosition(float position)
+ {
+ position = Math.Clamp(position, 0, 1);
+
+ if (position == X)
+ return;
+
+ Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y);
+ X = position;
+ }
+
///
/// Drop any fruit off the plate.
///
public void Drop()
{
- var fruit = caughtFruit.ToArray();
-
- foreach (var f in fruit)
- {
- if (ExplodingFruitTarget != null)
- {
- f.Anchor = Anchor.TopLeft;
- f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
-
- caughtFruit.Remove(f);
-
- ExplodingFruitTarget.Add(f);
- }
-
- f.MoveToY(f.Y + 75, 750, Easing.InSine);
- f.FadeOut(750);
-
- // todo: this shouldn't exist once DrawableHitObject's ClearTransformsAfter overrides are repaired.
- f.LifetimeStart = Time.Current;
- f.Expire();
- }
+ foreach (var f in caughtFruit.ToArray())
+ Drop(f);
}
///
@@ -425,10 +428,26 @@ namespace osu.Game.Rulesets.Catch.UI
Explode(f);
}
+ public void Drop(DrawableHitObject fruit) => removeFromPlateWithTransform(fruit, f =>
+ {
+ f.MoveToY(f.Y + 75, 750, Easing.InSine);
+ f.FadeOut(750);
+ });
+
public void Explode(DrawableHitObject fruit)
{
var originalX = fruit.X * Scale.X;
+ removeFromPlateWithTransform(fruit, f =>
+ {
+ f.MoveToY(f.Y - 50, 250, Easing.OutSine).Then().MoveToY(f.Y + 50, 500, Easing.InSine);
+ f.MoveToX(f.X + originalX * 6, 1000);
+ f.FadeOut(750);
+ });
+ }
+
+ private void removeFromPlateWithTransform(DrawableHitObject fruit, Action action)
+ {
if (ExplodingFruitTarget != null)
{
fruit.Anchor = Anchor.TopLeft;
@@ -442,26 +461,127 @@ namespace osu.Game.Rulesets.Catch.UI
ExplodingFruitTarget.Add(fruit);
}
- fruit.ClearTransforms();
- fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine).Then().MoveToY(fruit.Y + 50, 500, Easing.InSine);
- fruit.MoveToX(fruit.X + originalX * 6, 1000);
- fruit.FadeOut(750);
+ double actionTime = Clock.CurrentTime;
- // todo: this shouldn't exist once DrawableHitObject's ClearTransformsAfter overrides are repaired.
- fruit.LifetimeStart = Time.Current;
- fruit.Expire();
- }
+ fruit.ApplyCustomUpdateState += onFruitOnApplyCustomUpdateState;
+ onFruitOnApplyCustomUpdateState(fruit, fruit.State.Value);
- public void UpdatePosition(float position)
- {
- position = Math.Clamp(position, 0, 1);
+ void onFruitOnApplyCustomUpdateState(DrawableHitObject o, ArmedState state)
+ {
+ using (fruit.BeginAbsoluteSequence(actionTime))
+ action(fruit);
- if (position == X)
- return;
-
- Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y);
- X = position;
+ fruit.Expire();
+ }
}
}
}
+
+ public class HitExplosion : CompositeDrawable
+ {
+ private readonly CircularContainer largeFaint;
+
+ public HitExplosion(DrawableCatchHitObject fruit)
+ {
+ Size = new Vector2(20);
+ Anchor = Anchor.TopCentre;
+ Origin = Anchor.BottomCentre;
+
+ Color4 objectColour = fruit.AccentColour.Value;
+
+ // scale roughly in-line with visual appearance of notes
+
+ const float angle_variangle = 15; // should be less than 45
+
+ const float roundness = 100;
+
+ const float initial_height = 10;
+
+ var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1);
+
+ InternalChildren = new Drawable[]
+ {
+ largeFaint = new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ // we want our size to be very small so the glow dominates it.
+ Size = new Vector2(0.8f),
+ Blending = BlendingParameters.Additive,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f),
+ Roundness = 160,
+ Radius = 200,
+ },
+ },
+ new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Blending = BlendingParameters.Additive,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1),
+ Roundness = 20,
+ Radius = 50,
+ },
+ },
+ new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Size = new Vector2(0.01f, initial_height),
+ Blending = BlendingParameters.Additive,
+ Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = colour,
+ Roundness = roundness,
+ Radius = 40,
+ },
+ },
+ new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Size = new Vector2(0.01f, initial_height),
+ Blending = BlendingParameters.Additive,
+ Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = colour,
+ Roundness = roundness,
+ Radius = 40,
+ },
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ const double duration = 400;
+
+ largeFaint
+ .ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
+ .FadeOut(duration * 2);
+
+ this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
+ Expire(true);
+ }
+ }
}
diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
index fdd820b891..fd8a1d175d 100644
--- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
@@ -8,7 +8,7 @@ using osu.Game.Configuration;
using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Catch.Objects.Drawable;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
diff --git a/osu.Game.Rulesets.Mania.Tests/SkinnableTestScene.cs b/osu.Game.Rulesets.Mania.Tests/SkinnableTestScene.cs
deleted file mode 100644
index 80b1b3df8e..0000000000
--- a/osu.Game.Rulesets.Mania.Tests/SkinnableTestScene.cs
+++ /dev/null
@@ -1,46 +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 osu.Framework.Allocation;
-using osu.Framework.Audio;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Game.Skinning;
-using osu.Game.Tests.Visual;
-
-namespace osu.Game.Rulesets.Mania.Tests
-{
- public abstract class SkinnableTestScene : OsuGridTestScene
- {
- private Skin defaultSkin;
-
- protected SkinnableTestScene()
- : base(1, 2)
- {
- }
-
- [BackgroundDependencyLoader]
- private void load(AudioManager audio, SkinManager skinManager)
- {
- defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
- }
-
- public void SetContents(Func creationFunction)
- {
- Cell(0).Child = createProvider(null, creationFunction);
- Cell(1).Child = createProvider(defaultSkin, creationFunction);
- }
-
- private Drawable createProvider(Skin skin, Func creationFunction)
- {
- var mainProvider = new SkinProvidingContainer(skin);
-
- return mainProvider
- .WithChild(new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider))
- {
- Child = creationFunction()
- });
- }
- }
-}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableJudgement.cs
index eea1a36a19..692d079c16 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableJudgement.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableJudgement.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
+using osu.Game.Tests.Visual;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
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 dea6e6c0fb..6855b99f28 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs
index bcbc1ee527..7bbde400ea 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs
@@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
{
base.UpdatePosition(screenSpacePosition);
- if (PlacementBegun)
+ if (PlacementActive)
{
var endTime = TimeAt(screenSpacePosition);
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs
index 7a3b42914e..a3657d3bb9 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs
@@ -56,13 +56,13 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
protected override void OnMouseUp(MouseUpEvent e)
{
- EndPlacement();
+ EndPlacement(true);
base.OnMouseUp(e);
}
public override void UpdatePosition(Vector2 screenSpacePosition)
{
- if (!PlacementBegun)
+ if (!PlacementActive)
Column = ColumnAt(screenSpacePosition);
if (Column == null) return;
diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteJudgement.cs
deleted file mode 100644
index e8b48768a1..0000000000
--- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteJudgement.cs
+++ /dev/null
@@ -1,14 +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 osu.Game.Rulesets.Scoring;
-
-namespace osu.Game.Rulesets.Mania.Judgements
-{
- public class HoldNoteJudgement : ManiaJudgement
- {
- public override bool AffectsCombo => false;
-
- protected override int NumericResultFor(HitResult result) => 0;
- }
-}
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs
index 6893e1e73b..86a00271e9 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs
@@ -3,8 +3,8 @@
using System;
using osu.Framework.Bindables;
-using osu.Framework.Caching;
using osu.Framework.Graphics;
+using osu.Framework.Layout;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
using osuTK;
@@ -22,21 +22,13 @@ namespace osu.Game.Rulesets.Mania.Mods
private class ManiaFlashlight : Flashlight
{
- private readonly Cached flashlightProperties = new Cached();
+ private readonly LayoutValue flashlightProperties = new LayoutValue(Invalidation.DrawSize);
public ManiaFlashlight()
{
FlashlightSize = new Vector2(0, default_flashlight_size);
- }
- public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
- {
- if ((invalidation & Invalidation.DrawSize) > 0)
- {
- flashlightProperties.Invalidate();
- }
-
- return base.Invalidate(invalidation, source, shallPropagate);
+ AddLayout(flashlightProperties);
}
protected override void Update()
diff --git a/osu.Game.Rulesets.Mania/Objects/BarLine.cs b/osu.Game.Rulesets.Mania/Objects/BarLine.cs
index 0981b028b2..09a746042b 100644
--- a/osu.Game.Rulesets.Mania/Objects/BarLine.cs
+++ b/osu.Game.Rulesets.Mania/Objects/BarLine.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 osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Objects
@@ -8,5 +9,7 @@ namespace osu.Game.Rulesets.Mania.Objects
public class BarLine : ManiaHitObject, IBarLine
{
public bool Major { get; set; }
+
+ public override Judgement CreateJudgement() => new IgnoreJudgement();
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
index 56bc797c7f..08b5b75f9c 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
@@ -71,8 +71,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
}
- protected override void UpdateStateTransforms(ArmedState state)
- {
- }
+ protected override void UpdateStateTransforms(ArmedState state) => this.FadeOut(150);
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
index 31a4857805..43f9ae2783 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
@@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osu.Framework.Caching;
using osuTK.Graphics;
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.Layout;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
@@ -65,6 +65,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
}
};
+
+ AddLayout(subtractionCache);
}
protected override void LoadComplete()
@@ -100,15 +102,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
}
- private readonly Cached subtractionCache = new Cached();
-
- public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
- {
- if ((invalidation & Invalidation.DrawSize) > 0)
- subtractionCache.Invalidate();
-
- return base.Invalidate(invalidation, source, shallPropagate);
- }
+ private readonly LayoutValue subtractionCache = new LayoutValue(Invalidation.DrawSize);
protected override void Update()
{
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
index 86d3d2b2b0..049bf55f90 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
@@ -4,7 +4,6 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
@@ -103,7 +102,7 @@ namespace osu.Game.Rulesets.Mania.Objects
}
}
- public override Judgement CreateJudgement() => new HoldNoteJudgement();
+ public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
index a28de7ea58..bfe9f1085b 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
@@ -115,9 +115,8 @@ namespace osu.Game.Rulesets.Mania.UI
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
- AutoSizeAxes = Axes.Both,
+ RelativeSizeAxes = Axes.Both,
Y = HIT_TARGET_POSITION + 150,
- BypassAutoSizeAxes = Axes.Both
},
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
}
diff --git a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs
deleted file mode 100644
index d4c3000d3f..0000000000
--- a/osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.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;
-using System.Text.RegularExpressions;
-using osu.Framework.Allocation;
-using osu.Framework.Audio;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Textures;
-using osu.Framework.IO.Stores;
-using osu.Game.Skinning;
-using osu.Game.Tests.Visual;
-
-namespace osu.Game.Rulesets.Osu.Tests
-{
- public abstract class SkinnableTestScene : OsuGridTestScene
- {
- private Skin metricsSkin;
- private Skin defaultSkin;
- private Skin specialSkin;
- private Skin oldSkin;
-
- protected SkinnableTestScene()
- : base(2, 3)
- {
- }
-
- [BackgroundDependencyLoader]
- private void load(AudioManager audio, SkinManager skinManager)
- {
- var dllStore = new DllResourceStore(typeof(SkinnableTestScene).Assembly);
-
- metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/metrics_skin"), audio, true);
- defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
- specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/special_skin"), audio, true);
- oldSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore(dllStore, "Resources/old_skin"), audio, true);
- }
-
- public void SetContents(Func creationFunction)
- {
- Cell(0).Child = createProvider(null, creationFunction);
- Cell(1).Child = createProvider(metricsSkin, creationFunction);
- Cell(2).Child = createProvider(defaultSkin, creationFunction);
- Cell(3).Child = createProvider(specialSkin, creationFunction);
- Cell(4).Child = createProvider(oldSkin, creationFunction);
- }
-
- private Drawable createProvider(Skin skin, Func creationFunction)
- {
- var mainProvider = new SkinProvidingContainer(skin);
-
- return mainProvider
- .WithChild(new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider))
- {
- Child = creationFunction()
- });
- }
-
- private class TestLegacySkin : LegacySkin
- {
- private readonly bool extrapolateAnimations;
-
- public TestLegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, bool extrapolateAnimations)
- : base(skin, storage, audioManager, "skin.ini")
- {
- this.extrapolateAnimations = extrapolateAnimations;
- }
-
- public override Texture GetTexture(string componentName)
- {
- // extrapolate frames to test longer animations
- if (extrapolateAnimations)
- {
- var match = Regex.Match(componentName, "-([0-9]*)");
-
- if (match.Length > 0 && int.TryParse(match.Groups[1].Value, out var number) && number < 60)
- return base.GetTexture(componentName.Replace($"-{number}", $"-{number % 2}"));
- }
-
- return base.GetTexture(componentName);
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
index ac627aa23e..02d4406809 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
@@ -10,6 +10,7 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
index aa170eae1e..7b96e2ec6a 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
@@ -7,7 +7,10 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing.Input;
+using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.UI.Cursor;
+using osu.Game.Screens.Play;
+using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
@@ -21,12 +24,50 @@ namespace osu.Game.Rulesets.Osu.Tests
typeof(CursorTrail)
};
- [BackgroundDependencyLoader]
- private void load()
+ [Cached]
+ private GameplayBeatmap gameplayBeatmap;
+
+ private ClickingCursorContainer lastContainer;
+
+ [Resolved]
+ private OsuConfigManager config { get; set; }
+
+ public TestSceneGameplayCursor()
+ {
+ gameplayBeatmap = new GameplayBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
+ }
+
+ [TestCase(1, 1)]
+ [TestCase(5, 1)]
+ [TestCase(10, 1)]
+ [TestCase(1, 1.5f)]
+ [TestCase(5, 1.5f)]
+ [TestCase(10, 1.5f)]
+ public void TestSizing(int circleSize, float userScale)
+ {
+ AddStep($"set user scale to {userScale}", () => config.Set(OsuSetting.GameplayCursorSize, userScale));
+ AddStep($"adjust cs to {circleSize}", () => gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize);
+ AddStep("turn on autosizing", () => config.Set(OsuSetting.AutoCursorSize, true));
+
+ AddStep("load content", loadContent);
+
+ AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize) * userScale);
+
+ AddStep("set user scale to 1", () => config.Set(OsuSetting.GameplayCursorSize, 1f));
+ AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize));
+
+ AddStep("turn off autosizing", () => config.Set(OsuSetting.AutoCursorSize, false));
+ AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == 1);
+
+ AddStep($"set user scale to {userScale}", () => config.Set(OsuSetting.GameplayCursorSize, userScale));
+ AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == userScale);
+ }
+
+ private void loadContent()
{
SetContents(() => new MovingCursorInputManager
{
- Child = new ClickingCursorContainer
+ Child = lastContainer = new ClickingCursorContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
index 098e277fff..ae5a28217c 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
@@ -14,6 +14,7 @@ using osu.Game.Rulesets.Mods;
using System.Linq;
using NUnit.Framework;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index e8386363be..defd3a6f22 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -22,6 +22,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
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 9d4e016eae..217707b180 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs
index bb47c7e464..407f5f540e 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs
@@ -30,12 +30,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
protected override bool OnClick(ClickEvent e)
{
- EndPlacement();
+ EndPlacement(true);
return true;
}
public override void UpdatePosition(Vector2 screenSpacePosition)
{
+ BeginPlacement();
HitObject.Position = ToLocalSpace(screenSpacePosition);
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
index 90512849d4..a780653796 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
@@ -68,6 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
switch (state)
{
case PlacementState.Initial:
+ BeginPlacement();
HitObject.Position = ToLocalSpace(screenSpacePosition);
break;
@@ -125,14 +126,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private void beginCurve()
{
- BeginPlacement();
+ BeginPlacement(commitStart: true);
setState(PlacementState.Body);
}
private void endCurve()
{
updateSlider();
- EndPlacement();
+ EndPlacement(true);
}
protected override void Update()
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs
index 48c1ce11e0..74b563d922 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs
@@ -45,14 +45,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
return false;
HitObject.EndTime = EditorClock.CurrentTime;
- EndPlacement();
+ EndPlacement(true);
}
else
{
if (e.Button != MouseButton.Left)
return false;
- BeginPlacement();
+ BeginPlacement(commitStart: true);
piece.FadeTo(1f, 150, Easing.OutQuint);
isPlacingEnd = true;
diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs
index 22b4c3e82e..a8719e0aa8 100644
--- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs
+++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs
@@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.Edit
if (existing == null)
return;
+ hitObject.RemoveTransform(existing);
+
using (hitObject.BeginAbsoluteSequence(existing.StartTime))
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
break;
diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs
deleted file mode 100644
index 5104d9494b..0000000000
--- a/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs
+++ /dev/null
@@ -1,14 +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 osu.Game.Rulesets.Scoring;
-
-namespace osu.Game.Rulesets.Osu.Judgements
-{
- public class OsuSliderTailJudgement : OsuJudgement
- {
- public override bool AffectsCombo => false;
-
- protected override int NumericResultFor(HitResult result) => 0;
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs
index 7eee71be81..75de6896a3 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs
@@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModDifficultyAdjust : ModDifficultyAdjust
{
- [SettingSource("Circle Size", "Override a beatmap's set CS.")]
+ [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)]
public BindableNumber CircleSize { get; } = new BindableFloat
{
Precision = 0.1f,
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
Value = 5,
};
- [SettingSource("Approach Rate", "Override a beatmap's set AR.")]
+ [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)]
public BindableNumber ApproachRate { get; } = new BindableFloat
{
Precision = 0.1f,
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
index 649b01c132..6286c80d7c 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
@@ -9,6 +9,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.UI;
using static osu.Game.Input.Handlers.ReplayInputHandler;
@@ -19,69 +20,18 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
- public void Update(Playfield playfield)
- {
- bool requiresHold = false;
- bool requiresHit = false;
+ ///
+ /// How early before a hitobject's start time to trigger a hit.
+ ///
+ private const float relax_leniency = 3;
- const float relax_leniency = 3;
-
- foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
- {
- if (!(drawable is DrawableOsuHitObject osuHit))
- continue;
-
- double time = osuHit.Clock.CurrentTime;
- double relativetime = time - osuHit.HitObject.StartTime;
-
- if (time < osuHit.HitObject.StartTime - relax_leniency) continue;
-
- if ((osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime) || osuHit.IsHit)
- continue;
-
- if (osuHit is DrawableHitCircle && osuHit.IsHovered)
- {
- Debug.Assert(osuHit.HitObject.HitWindows != null);
- requiresHit |= osuHit.HitObject.HitWindows.CanBeHit(relativetime);
- }
-
- requiresHold |= (osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered)) || osuHit is DrawableSpinner;
- }
-
- if (requiresHit)
- {
- addAction(false);
- addAction(true);
- }
-
- addAction(requiresHold);
- }
-
- private bool wasHit;
+ private bool isDownState;
private bool wasLeft;
private OsuInputManager osuInputManager;
- private void addAction(bool hitting)
- {
- if (wasHit == hitting)
- return;
-
- wasHit = hitting;
-
- var state = new ReplayState
- {
- PressedActions = new List()
- };
-
- if (hitting)
- {
- state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
- wasLeft = !wasLeft;
- }
-
- state.Apply(osuInputManager.CurrentState, osuInputManager);
- }
+ private ReplayState state;
+ private double lastStateChangeTime;
public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
{
@@ -89,5 +39,85 @@ namespace osu.Game.Rulesets.Osu.Mods
osuInputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager;
osuInputManager.AllowUserPresses = false;
}
+
+ public void Update(Playfield playfield)
+ {
+ bool requiresHold = false;
+ bool requiresHit = false;
+
+ double time = playfield.Clock.CurrentTime;
+
+ foreach (var h in playfield.HitObjectContainer.AliveObjects.OfType())
+ {
+ // we are not yet close enough to the object.
+ if (time < h.HitObject.StartTime - relax_leniency)
+ break;
+
+ // already hit or beyond the hittable end time.
+ if (h.IsHit || (h.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime))
+ continue;
+
+ switch (h)
+ {
+ case DrawableHitCircle circle:
+ handleHitCircle(circle);
+ break;
+
+ case DrawableSlider slider:
+ // Handles cases like "2B" beatmaps, where sliders may be overlapping and simply holding is not enough.
+ if (!slider.HeadCircle.IsHit)
+ handleHitCircle(slider.HeadCircle);
+
+ requiresHold |= slider.Ball.IsHovered || h.IsHovered;
+ break;
+
+ case DrawableSpinner _:
+ requiresHold = true;
+ break;
+ }
+ }
+
+ if (requiresHit)
+ {
+ changeState(false);
+ changeState(true);
+ }
+
+ if (requiresHold)
+ changeState(true);
+ else if (isDownState && time - lastStateChangeTime > AutoGenerator.KEY_UP_DELAY)
+ changeState(false);
+
+ void handleHitCircle(DrawableHitCircle circle)
+ {
+ if (!circle.IsHovered)
+ return;
+
+ Debug.Assert(circle.HitObject.HitWindows != null);
+ requiresHit |= circle.HitObject.HitWindows.CanBeHit(time - circle.HitObject.StartTime);
+ }
+
+ void changeState(bool down)
+ {
+ if (isDownState == down)
+ return;
+
+ isDownState = down;
+ lastStateChangeTime = time;
+
+ state = new ReplayState
+ {
+ PressedActions = new List()
+ };
+
+ if (down)
+ {
+ state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
+ wasLeft = !wasLeft;
+ }
+
+ state?.Apply(osuInputManager.CurrentState, osuInputManager);
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
index cc664ae72e..41daef1f38 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK;
namespace osu.Game.Rulesets.Osu.Mods
@@ -27,26 +28,40 @@ namespace osu.Game.Rulesets.Osu.Mods
public void ApplyToDrawableHitObjects(IEnumerable drawables)
{
foreach (var drawable in drawables)
+ drawable.ApplyCustomUpdateState += applyTransform;
+ }
+
+ private void applyTransform(DrawableHitObject drawable, ArmedState state)
+ {
+ switch (drawable)
{
- var hitObject = (OsuHitObject)drawable.HitObject;
+ case DrawableSliderHead _:
+ case DrawableSliderTail _:
+ case DrawableSliderTick _:
+ case DrawableRepeatPoint _:
+ return;
- float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2;
+ default:
+ var hitObject = (OsuHitObject)drawable.HitObject;
- Vector2 originalPosition = drawable.Position;
- Vector2 appearOffset = new Vector2(MathF.Cos(theta), MathF.Sin(theta)) * appearDistance;
+ float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2;
- //the - 1 and + 1 prevents the hit objects to appear in the wrong position.
- double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1;
- double moveDuration = hitObject.TimePreempt + 1;
+ Vector2 originalPosition = drawable.Position;
+ Vector2 appearOffset = new Vector2(MathF.Cos(theta), MathF.Sin(theta)) * appearDistance;
- using (drawable.BeginAbsoluteSequence(appearTime, true))
- {
- drawable
- .MoveToOffset(appearOffset)
- .MoveTo(originalPosition, moveDuration, Easing.InOutSine);
- }
+ //the - 1 and + 1 prevents the hit objects to appear in the wrong position.
+ double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1;
+ double moveDuration = hitObject.TimePreempt + 1;
- theta += (float)hitObject.TimeFadeIn / 1000;
+ using (drawable.BeginAbsoluteSequence(appearTime, true))
+ {
+ drawable
+ .MoveToOffset(appearOffset)
+ .MoveTo(originalPosition, moveDuration, Easing.InOutSine);
+ }
+
+ theta += (float)hitObject.TimeFadeIn / 1000;
+ break;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
index a5e89210f6..921b23cb13 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
@@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
private const int spacing = 32;
private const double preempt = 800;
+ public override bool RemoveWhenNotAlive => false;
+
///
/// The start time of .
///
@@ -79,27 +81,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
drawableObject.HitObject.DefaultsApplied += scheduleRefresh;
}
- private void scheduleRefresh() => Scheduler.AddOnce(refresh);
+ private void scheduleRefresh()
+ {
+ Scheduler.AddOnce(refresh);
+ }
private void refresh()
{
ClearInternal();
- if (End == null)
- return;
-
OsuHitObject osuStart = Start.HitObject;
- OsuHitObject osuEnd = End.HitObject;
+ double startTime = osuStart.GetEndTime();
- if (osuEnd.NewCombo)
- return;
+ LifetimeStart = startTime;
- if (osuStart is Spinner || osuEnd is Spinner)
+ OsuHitObject osuEnd = End?.HitObject;
+
+ if (osuEnd == null || osuEnd.NewCombo || osuStart is Spinner || osuEnd is Spinner)
+ {
+ // ensure we always set a lifetime for full LifetimeManagementContainer benefits
+ LifetimeEnd = LifetimeStart;
return;
+ }
Vector2 startPosition = osuStart.EndPosition;
Vector2 endPosition = osuEnd.Position;
- double startTime = osuStart.GetEndTime();
double endTime = osuEnd.StartTime;
Vector2 distanceVector = endPosition - startPosition;
@@ -107,6 +113,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI));
double duration = endTime - startTime;
+ double? firstTransformStartTime = null;
+ double finalTransformEndTime = startTime;
+
for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
{
float fraction = (float)d / distance;
@@ -125,16 +134,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Scale = new Vector2(1.5f * osuEnd.Scale),
});
+ if (firstTransformStartTime == null)
+ firstTransformStartTime = fadeInTime;
+
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(osuEnd.TimeFadeIn);
fp.ScaleTo(osuEnd.Scale, osuEnd.TimeFadeIn, Easing.Out);
fp.MoveTo(pointEndPosition, osuEnd.TimeFadeIn, Easing.Out);
fp.Delay(fadeOutTime - fadeInTime).FadeOut(osuEnd.TimeFadeIn);
- }
- fp.Expire(true);
+ finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn;
+ }
}
+
+ // todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed.
+ LifetimeStart = firstTransformStartTime ?? startTime;
+ LifetimeEnd = finalTransformEndTime;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
index be192080f9..4d73e711bb 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
///
/// Visualises connections between s.
///
- public class FollowPointRenderer : CompositeDrawable
+ public class FollowPointRenderer : LifetimeManagementContainer
{
///
/// All the s contained by this .
@@ -45,8 +45,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// The index of in .
private void addConnection(FollowPointConnection connection)
{
- AddInternal(connection);
-
// Groups are sorted by their start time when added such that the index can be used to post-process other surrounding connections
int index = connections.AddInPlace(connection, Comparer.Create((g1, g2) => g1.StartTime.Value.CompareTo(g2.StartTime.Value)));
@@ -74,6 +72,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPointConnection previousConnection = connections[index - 1];
previousConnection.End = connection.Start;
}
+
+ AddInternal(connection);
}
///
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 0dc5c9b4a0..c871089acd 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -4,6 +4,7 @@
using System;
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;
@@ -214,9 +215,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public class DefaultSliderBall : CompositeDrawable
{
+ private Box box;
+
[BackgroundDependencyLoader]
private void load(DrawableHitObject drawableObject, ISkinSource skin)
{
+ var slider = (DrawableSlider)drawableObject;
+
RelativeSizeAxes = Axes.Both;
float radius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
@@ -231,14 +236,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
BorderThickness = 10,
BorderColour = Color4.White,
Alpha = 1,
- Child = new Box
+ Child = box = new Box
{
+ Blending = BlendingParameters.Additive,
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
- Alpha = 0.4f,
+ AlwaysPresent = true,
+ Alpha = 0
}
};
+
+ slider.Tracking.BindValueChanged(trackingChanged, true);
}
+
+ private void trackingChanged(ValueChangedEvent tracking) =>
+ box.FadeTo(tracking.NewValue ? 0.6f : 0.05f, 200, Easing.OutQuint);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 95fb6d9d48..77f8ec6cc8 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -116,8 +116,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public Slider()
{
- SamplesBindable.ItemsAdded += _ => updateNestedSamples();
- SamplesBindable.ItemsRemoved += _ => updateNestedSamples();
+ SamplesBindable.CollectionChanged += (_, __) => updateNestedSamples();
Path.Version.ValueChanged += _ => updateNestedPositions();
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
index c17d2275b8..127c36fcc0 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
@@ -4,7 +4,6 @@
using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
@@ -23,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects
pathVersion.BindValueChanged(_ => Position = slider.EndPosition);
}
- public override Judgement CreateJudgement() => new OsuSliderTailJudgement();
+ public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 4e86662ec6..37df5ec540 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -5,7 +5,6 @@ using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using osu.Framework.Allocation;
-using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Batches;
using osu.Framework.Graphics.OpenGL.Vertices;
@@ -14,6 +13,7 @@ using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Input.Events;
+using osu.Framework.Layout;
using osu.Framework.Timing;
using osuTK;
using osuTK.Graphics;
@@ -43,6 +43,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
// -1 signals that the part is unusable, and should not be drawn
parts[i].InvalidationID = -1;
}
+
+ AddLayout(partSizeCache);
}
[BackgroundDependencyLoader]
@@ -72,20 +74,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
}
}
- private readonly Cached partSizeCache = new Cached();
+ private readonly LayoutValue partSizeCache = new LayoutValue(Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence);
private Vector2 partSize => partSizeCache.IsValid
? partSizeCache.Value
: (partSizeCache.Value = new Vector2(Texture.DisplayWidth, Texture.DisplayHeight) * DrawInfo.Matrix.ExtractScale().Xy);
- public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
- {
- if ((invalidation & (Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence)) > 0)
- partSizeCache.Invalidate();
-
- return base.Invalidate(invalidation, source, shallPropagate);
- }
-
///
/// The amount of time to fade the cursor trail pieces.
///
@@ -97,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
base.Update();
- Invalidate(Invalidation.DrawNode, shallPropagate: false);
+ Invalidate(Invalidation.DrawNode);
const int fade_clock_reset_threshold = 1000000;
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
index 79b5d1b7f8..28600ef55b 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
@@ -12,6 +12,7 @@ using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.UI;
+using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osuTK;
@@ -29,10 +30,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly Drawable cursorTrail;
- public Bindable CursorScale;
+ public Bindable CursorScale = new BindableFloat(1);
+
private Bindable userCursorScale;
private Bindable autoCursorScale;
- private readonly IBindable beatmap = new Bindable();
public OsuCursorContainer()
{
@@ -43,37 +44,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
};
}
+ [Resolved(canBeNull: true)]
+ private GameplayBeatmap beatmap { get; set; }
+
+ [Resolved]
+ private OsuConfigManager config { get; set; }
+
[BackgroundDependencyLoader(true)]
- private void load(OsuConfigManager config, OsuRulesetConfigManager rulesetConfig, IBindable beatmap)
+ private void load(OsuConfigManager config, OsuRulesetConfigManager rulesetConfig)
{
rulesetConfig?.BindWith(OsuRulesetSetting.ShowCursorTrail, showTrail);
-
- this.beatmap.BindTo(beatmap);
- this.beatmap.ValueChanged += _ => calculateScale();
-
- userCursorScale = config.GetBindable(OsuSetting.GameplayCursorSize);
- userCursorScale.ValueChanged += _ => calculateScale();
-
- autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize);
- autoCursorScale.ValueChanged += _ => calculateScale();
-
- CursorScale = new BindableFloat();
- CursorScale.ValueChanged += e => ActiveCursor.Scale = cursorTrail.Scale = new Vector2(e.NewValue);
-
- calculateScale();
- }
-
- private void calculateScale()
- {
- float scale = userCursorScale.Value;
-
- if (autoCursorScale.Value && beatmap.Value != null)
- {
- // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
- scale *= 1f - 0.7f * (1f + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY;
- }
-
- CursorScale.Value = scale;
}
protected override void LoadComplete()
@@ -81,6 +61,46 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
base.LoadComplete();
showTrail.BindValueChanged(v => cursorTrail.FadeTo(v.NewValue ? 1 : 0, 200), true);
+
+ userCursorScale = config.GetBindable(OsuSetting.GameplayCursorSize);
+ userCursorScale.ValueChanged += _ => calculateScale();
+
+ autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize);
+ autoCursorScale.ValueChanged += _ => calculateScale();
+
+ CursorScale.ValueChanged += e =>
+ {
+ var newScale = new Vector2(e.NewValue);
+
+ ActiveCursor.Scale = newScale;
+ cursorTrail.Scale = newScale;
+ };
+
+ calculateScale();
+ }
+
+ ///
+ /// Get the scale applicable to the ActiveCursor based on a beatmap's circle size.
+ ///
+ public static float GetScaleForCircleSize(float circleSize) =>
+ 1f - 0.7f * (1f + circleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY;
+
+ private void calculateScale()
+ {
+ float scale = userCursorScale.Value;
+
+ if (autoCursorScale.Value && beatmap != null)
+ {
+ // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
+ scale *= GetScaleForCircleSize(beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
+ }
+
+ CursorScale.Value = scale;
+
+ var newScale = new Vector2(scale);
+
+ ActiveCursor.ScaleTo(newScale, 400, Easing.OutQuint);
+ cursorTrail.Scale = newScale;
}
private int downCount;
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs
index f27e329e8e..ccacc50de1 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneSwellJudgements.cs
@@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
@@ -28,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
public void TestZeroTickTimeOffsets()
{
AddUntilStep("gameplay finished", () => Player.ScoreProcessor.HasCompleted);
- AddAssert("all tick offsets are 0", () => Player.Results.Where(r => r.Judgement is TaikoSwellTickJudgement).All(r => r.TimeOffset == 0));
+ AddAssert("all tick offsets are 0", () => Player.Results.Where(r => r.HitObject is SwellTick).All(r => r.TimeOffset == 0));
}
protected override bool Autoplay => true;
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 d728d65bfd..f6054a5d6f 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs
index b7db3307ad..1253b7c8ae 100644
--- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs
+++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs
@@ -2,8 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
-using osu.Framework.Caching;
using osu.Framework.Graphics;
+using osu.Framework.Layout;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.UI;
@@ -30,13 +30,15 @@ namespace osu.Game.Rulesets.Taiko.Mods
private class TaikoFlashlight : Flashlight
{
- private readonly Cached flashlightProperties = new Cached();
+ private readonly LayoutValue flashlightProperties = new LayoutValue(Invalidation.DrawSize);
private readonly TaikoPlayfield taikoPlayfield;
public TaikoFlashlight(TaikoPlayfield taikoPlayfield)
{
this.taikoPlayfield = taikoPlayfield;
FlashlightSize = new Vector2(0, getSizeFor(0));
+
+ AddLayout(flashlightProperties);
}
private float getSizeFor(int combo)
@@ -56,16 +58,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
protected override string FragmentShader => "CircularFlashlight";
- public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
- {
- if ((invalidation & Invalidation.DrawSize) > 0)
- {
- flashlightProperties.Invalidate();
- }
-
- return base.Invalidate(invalidation, source, shallPropagate);
- }
-
protected override void Update()
{
base.Update();
diff --git a/osu.Game.Rulesets.Taiko/Objects/BarLine.cs b/osu.Game.Rulesets.Taiko/Objects/BarLine.cs
index 2afbbc737c..6306195704 100644
--- a/osu.Game.Rulesets.Taiko/Objects/BarLine.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/BarLine.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 osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Taiko.Objects
@@ -8,5 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
public class BarLine : TaikoHitObject, IBarLine
{
public bool Major { get; set; }
+
+ public override Judgement CreateJudgement() => new IgnoreJudgement();
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
index 1a5a797f28..e9caabbcc8 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
@@ -54,5 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Alpha = 0.75f
});
}
+
+ protected override void UpdateStateTransforms(ArmedState state) => this.FadeOut(150);
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
index 91f4d3dbe7..bdc0478195 100644
--- a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
@@ -3,13 +3,12 @@
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
{
public class SwellTick : TaikoHitObject
{
- public override Judgement CreateJudgement() => new TaikoSwellTickJudgement();
+ public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs
index 244e37f017..885abb61b5 100644
--- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs
+++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs
@@ -154,6 +154,7 @@ namespace osu.Game.Tests.Gameplay
private class JudgeableHitObject : HitObject
{
public override Judgement CreateJudgement() => new Judgement();
+ protected override HitWindows CreateHitWindows() => new HitWindows();
}
}
}
diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs
index 17dc27543d..7a89642e11 100644
--- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs
+++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs
@@ -11,8 +11,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
using osu.Framework.Testing;
using osu.Game.Audio;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
@@ -78,7 +78,7 @@ namespace osu.Game.Tests.Gameplay
}
}
- private class TestHitObjectWithCombo : HitObject, IHasComboInformation
+ private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation
{
public bool NewCombo { get; } = false;
public int ComboOffset { get; } = 0;
diff --git a/osu.Game.Tests/Resources/Archives/ogg-beatmap.osz b/osu.Game.Tests/Resources/Archives/ogg-beatmap.osz
new file mode 100644
index 0000000000..f264a8dda2
Binary files /dev/null and b/osu.Game.Tests/Resources/Archives/ogg-beatmap.osz differ
diff --git a/osu.Game.Tests/Resources/Archives/ogg-skin.osk b/osu.Game.Tests/Resources/Archives/ogg-skin.osk
new file mode 100644
index 0000000000..d7379446aa
Binary files /dev/null and b/osu.Game.Tests/Resources/Archives/ogg-skin.osk differ
diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs
new file mode 100644
index 0000000000..4d3b73fb32
--- /dev/null
+++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs
@@ -0,0 +1,37 @@
+// 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.Audio.Track;
+using osu.Framework.Testing;
+using osu.Game.Audio;
+using osu.Game.Beatmaps;
+using osu.Game.IO.Archives;
+using osu.Game.Tests.Resources;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Tests.Skins
+{
+ [HeadlessTest]
+ public class TestSceneBeatmapSkinResources : OsuTestScene
+ {
+ [Resolved]
+ private BeatmapManager beatmaps { get; set; }
+
+ private WorkingBeatmap beatmap;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ var imported = beatmaps.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-beatmap.osz"))).Result;
+ beatmap = beatmaps.GetWorkingBeatmap(imported.Beatmaps[0]);
+ }
+
+ [Test]
+ public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo("sample")) != null);
+
+ [Test]
+ public void TestRetrieveOggTrack() => AddAssert("track is non-null", () => !(beatmap.Track is TrackVirtual));
+ }
+}
diff --git a/osu.Game.Tests/Skins/TestSceneSkinResources.cs b/osu.Game.Tests/Skins/TestSceneSkinResources.cs
new file mode 100644
index 0000000000..107a96292f
--- /dev/null
+++ b/osu.Game.Tests/Skins/TestSceneSkinResources.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 NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Testing;
+using osu.Game.Audio;
+using osu.Game.IO.Archives;
+using osu.Game.Skinning;
+using osu.Game.Tests.Resources;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Tests.Skins
+{
+ [HeadlessTest]
+ public class TestSceneSkinResources : OsuTestScene
+ {
+ [Resolved]
+ private SkinManager skins { get; set; }
+
+ private ISkin skin;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).Result;
+ skin = skins.GetSkin(imported);
+ }
+
+ [Test]
+ public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => skin.GetSample(new SampleInfo("sample")) != null);
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs
index 46f62b9541..b25b81c9af 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs
@@ -20,6 +20,7 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.UI;
@@ -289,7 +290,7 @@ namespace osu.Game.Tests.Visual.Gameplay
#region HitObject
- private class TestHitObject : HitObject, IHasEndTime
+ private class TestHitObject : ConvertHitObject, IHasEndTime
{
public double EndTime { get; set; }
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs
index 8904b54b0d..1527cba6fc 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs
@@ -2,16 +2,17 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
-using osu.Game.Rulesets.Objects;
using System;
using System.Collections.Generic;
using osu.Game.Rulesets.Judgements;
using osu.Framework.Utils;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Threading;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Mania.Scoring;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Scoring;
@@ -43,6 +44,22 @@ namespace osu.Game.Tests.Visual.Gameplay
AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20);
AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20);
AddStep("New fixed judgement (50ms)", () => newJudgement(50));
+
+ AddStep("Judgement barrage", () =>
+ {
+ int runCount = 0;
+
+ ScheduledDelegate del = null;
+
+ del = Scheduler.AddDelayed(() =>
+ {
+ newJudgement(runCount++ / 10f);
+
+ if (runCount == 500)
+ // ReSharper disable once AccessToModifiedClosure
+ del?.Cancel();
+ }, 10, true);
+ });
}
[Test]
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index 33ecbed62e..100f99d130 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -91,9 +91,44 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddStep("load dummy beatmap", () => ResetPlayer(false));
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
- AddRepeatStep("move mouse", () => InputManager.MoveMouseTo(loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft + (loader.VisualSettings.ScreenSpaceDrawQuad.BottomRight - loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft) * RNG.NextSingle()), 20);
+
+ AddUntilStep("wait for load ready", () =>
+ {
+ moveMouse();
+ return player.LoadState == LoadState.Ready;
+ });
+ AddRepeatStep("move mouse", moveMouse, 20);
+
AddAssert("loader still active", () => loader.IsCurrentScreen());
AddUntilStep("loads after idle", () => !loader.IsCurrentScreen());
+
+ void moveMouse()
+ {
+ InputManager.MoveMouseTo(
+ loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft
+ + (loader.VisualSettings.ScreenSpaceDrawQuad.BottomRight - loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft)
+ * RNG.NextSingle());
+ }
+ }
+
+ [Test]
+ public void TestBlockLoadViaFocus()
+ {
+ OsuFocusedOverlayContainer overlay = null;
+
+ AddStep("load dummy beatmap", () => ResetPlayer(false));
+ AddUntilStep("wait for current", () => loader.IsCurrentScreen());
+
+ AddStep("show focused overlay", () => { container.Add(overlay = new ChangelogOverlay { State = { Value = Visibility.Visible } }); });
+ AddUntilStep("overlay visible", () => overlay.IsPresent);
+
+ AddUntilStep("wait for load ready", () => player.LoadState == LoadState.Ready);
+ AddRepeatStep("twiddle thumbs", () => { }, 20);
+
+ AddAssert("loader still active", () => loader.IsCurrentScreen());
+
+ AddStep("hide overlay", () => overlay.Hide());
+ AddUntilStep("loads after idle", () => !loader.IsCurrentScreen());
}
[Test]
@@ -159,13 +194,22 @@ namespace osu.Game.Tests.Visual.Gameplay
}
[Test]
- public void TestMutedNotificationMasterVolume() => addVolumeSteps("master volume", () => audioManager.Volume.Value = 0, null, () => audioManager.Volume.IsDefault);
+ public void TestMutedNotificationMasterVolume()
+ {
+ addVolumeSteps("master volume", () => audioManager.Volume.Value = 0, null, () => audioManager.Volume.IsDefault);
+ }
[Test]
- public void TestMutedNotificationTrackVolume() => addVolumeSteps("music volume", () => audioManager.VolumeTrack.Value = 0, null, () => audioManager.VolumeTrack.IsDefault);
+ public void TestMutedNotificationTrackVolume()
+ {
+ addVolumeSteps("music volume", () => audioManager.VolumeTrack.Value = 0, null, () => audioManager.VolumeTrack.IsDefault);
+ }
[Test]
- public void TestMutedNotificationMuteButton() => addVolumeSteps("mute button", null, () => container.VolumeOverlay.IsMuted.Value = true, () => !container.VolumeOverlay.IsMuted.Value);
+ public void TestMutedNotificationMuteButton()
+ {
+ addVolumeSteps("mute button", null, () => container.VolumeOverlay.IsMuted.Value = true, () => !container.VolumeOverlay.IsMuted.Value);
+ }
///
/// Created for avoiding copy pasting code for the same steps.
@@ -179,7 +223,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce).Value = false);
AddStep("load player", () => ResetPlayer(false, beforeLoad, afterLoad));
- AddUntilStep("wait for player", () => player.IsLoaded);
+ AddUntilStep("wait for player", () => player.LoadState == LoadState.Ready);
AddAssert("check for notification", () => container.NotificationOverlay.UnreadCount.Value == 1);
AddStep("click notification", () =>
@@ -193,6 +237,8 @@ namespace osu.Game.Tests.Visual.Gameplay
});
AddAssert("check " + volumeName, assert);
+
+ AddUntilStep("wait for player load", () => player.IsLoaded);
}
private class TestPlayerLoaderContainer : Container
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs
new file mode 100644
index 0000000000..9fbe8f7ffe
--- /dev/null
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs
@@ -0,0 +1,255 @@
+// 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.Testing;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Screens.Multi;
+using osu.Game.Tests.Beatmaps;
+using osuTK;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Multiplayer
+{
+ public class TestSceneDrawableRoomPlaylist : ManualInputManagerTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DrawableRoomPlaylist),
+ typeof(DrawableRoomPlaylistItem)
+ };
+
+ private TestPlaylist playlist;
+
+ [Test]
+ public void TestNonEditableNonSelectable()
+ {
+ createPlaylist(false, false);
+
+ moveToItem(0);
+ assertHandleVisibility(0, false);
+ assertDeleteButtonVisibility(0, false);
+
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+ AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
+ }
+
+ [Test]
+ public void TestEditable()
+ {
+ createPlaylist(true, false);
+
+ moveToItem(0);
+ assertHandleVisibility(0, true);
+ assertDeleteButtonVisibility(0, true);
+
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+ AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
+ }
+
+ [Test]
+ public void TestSelectable()
+ {
+ createPlaylist(false, true);
+
+ moveToItem(0);
+ assertHandleVisibility(0, false);
+ assertDeleteButtonVisibility(0, false);
+
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+
+ AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
+ }
+
+ [Test]
+ public void TestEditableSelectable()
+ {
+ createPlaylist(true, true);
+
+ moveToItem(0);
+ assertHandleVisibility(0, true);
+ assertDeleteButtonVisibility(0, true);
+
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+
+ AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
+ }
+
+ [Test]
+ public void TestSelectionNotLostAfterRearrangement()
+ {
+ createPlaylist(true, true);
+
+ moveToItem(0);
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+
+ moveToDragger(0);
+ AddStep("begin drag", () => InputManager.PressButton(MouseButton.Left));
+ moveToDragger(1, new Vector2(0, 5));
+ AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left));
+
+ AddAssert("item 1 is selected", () => playlist.SelectedItem.Value == playlist.Items[1]);
+ }
+
+ [Test]
+ public void TestItemRemovedOnDeletion()
+ {
+ PlaylistItem selectedItem = null;
+
+ createPlaylist(true, true);
+
+ moveToItem(0);
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+ AddStep("retrieve selection", () => selectedItem = playlist.SelectedItem.Value);
+
+ moveToDeleteButton(0);
+ AddStep("click delete button", () => InputManager.Click(MouseButton.Left));
+
+ AddAssert("item removed", () => !playlist.Items.Contains(selectedItem));
+ }
+
+ [Test]
+ public void TestNextItemSelectedAfterDeletion()
+ {
+ createPlaylist(true, true);
+
+ moveToItem(0);
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+
+ moveToDeleteButton(0);
+ AddStep("click delete button", () => InputManager.Click(MouseButton.Left));
+
+ AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
+ }
+
+ [Test]
+ public void TestLastItemSelectedAfterLastItemDeleted()
+ {
+ createPlaylist(true, true);
+
+ AddWaitStep("wait for flow", 5); // Items may take 1 update frame to flow. A wait count of 5 is guaranteed to result in the flow being updated as desired.
+ AddStep("scroll to bottom", () => playlist.ChildrenOfType>().First().ScrollToEnd(false));
+
+ moveToItem(19);
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+
+ moveToDeleteButton(19);
+ AddStep("click delete button", () => InputManager.Click(MouseButton.Left));
+
+ AddAssert("item 18 is selected", () => playlist.SelectedItem.Value == playlist.Items[18]);
+ }
+
+ [Test]
+ public void TestSelectionResetWhenAllItemsDeleted()
+ {
+ createPlaylist(true, true);
+
+ AddStep("remove all but one item", () =>
+ {
+ playlist.Items.RemoveRange(1, playlist.Items.Count - 1);
+ });
+
+ moveToItem(0);
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+ moveToDeleteButton(0);
+ AddStep("click delete button", () => InputManager.Click(MouseButton.Left));
+
+ AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
+ }
+
+ // Todo: currently not possible due to bindable list shortcomings (https://github.com/ppy/osu-framework/issues/3081)
+ // [Test]
+ public void TestNextItemSelectedAfterExternalDeletion()
+ {
+ createPlaylist(true, true);
+
+ moveToItem(0);
+ AddStep("click", () => InputManager.Click(MouseButton.Left));
+ AddStep("remove item 0", () => playlist.Items.RemoveAt(0));
+
+ AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
+ }
+
+ [Test]
+ public void TestChangeBeatmapAndRemove()
+ {
+ createPlaylist(true, true);
+
+ AddStep("change beatmap of first item", () => playlist.Items[0].BeatmapID = 30);
+ moveToDeleteButton(0);
+ AddStep("click delete button", () => InputManager.Click(MouseButton.Left));
+ }
+
+ private void moveToItem(int index, Vector2? offset = null)
+ => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType>().ElementAt(index), offset));
+
+ private void moveToDragger(int index, Vector2? offset = null) => AddStep($"move mouse to dragger {index}", () =>
+ {
+ var item = playlist.ChildrenOfType>().ElementAt(index);
+ InputManager.MoveMouseTo(item.ChildrenOfType.PlaylistItemHandle>().Single(), offset);
+ });
+
+ private void moveToDeleteButton(int index, Vector2? offset = null) => AddStep($"move mouse to delete button {index}", () =>
+ {
+ var item = playlist.ChildrenOfType>().ElementAt(index);
+ InputManager.MoveMouseTo(item.ChildrenOfType().ElementAt(0), offset);
+ });
+
+ private void assertHandleVisibility(int index, bool visible)
+ => AddAssert($"handle {index} {(visible ? "is" : "is not")} visible",
+ () => (playlist.ChildrenOfType.PlaylistItemHandle>().ElementAt(index).Alpha > 0) == visible);
+
+ private void assertDeleteButtonVisibility(int index, bool visible)
+ => AddAssert($"delete button {index} {(visible ? "is" : "is not")} visible", () => (playlist.ChildrenOfType().ElementAt(2 + index * 2).Alpha > 0) == visible);
+
+ private void createPlaylist(bool allowEdit, bool allowSelection)
+ {
+ AddStep("create playlist", () =>
+ {
+ Child = playlist = new TestPlaylist(allowEdit, allowSelection)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(500, 300)
+ };
+
+ for (int i = 0; i < 20; i++)
+ {
+ playlist.Items.Add(new PlaylistItem
+ {
+ ID = i,
+ Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
+ Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RequiredMods =
+ {
+ new OsuModHardRock(),
+ new OsuModDoubleTime(),
+ new OsuModAutoplay()
+ }
+ });
+ }
+ });
+
+ AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded));
+ }
+
+ private class TestPlaylist : DrawableRoomPlaylist
+ {
+ public new IReadOnlyDictionary> ItemMap => base.ItemMap;
+
+ public TestPlaylist(bool allowEdit, bool allowSelection)
+ : base(allowEdit, allowSelection)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs
new file mode 100644
index 0000000000..1e1bc9725c
--- /dev/null
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs
@@ -0,0 +1,59 @@
+// 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.Graphics;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Online.Multiplayer.RoomStatuses;
+using osu.Game.Screens.Multi.Lounge.Components;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Multiplayer
+{
+ public class TestSceneLoungeRoomInfo : MultiplayerTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(RoomInfo)
+ };
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ Room.CopyFrom(new Room());
+
+ Child = new RoomInfo
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Width = 500
+ };
+ });
+
+ public override void SetUpSteps()
+ {
+ // Todo: Temp
+ }
+
+ [Test]
+ public void TestNonSelectedRoom()
+ {
+ AddStep("set null room", () => Room.RoomID.Value = null);
+ }
+
+ [Test]
+ public void TestOpenRoom()
+ {
+ AddStep("set open room", () =>
+ {
+ Room.RoomID.Value = 0;
+ Room.Name.Value = "Room 0";
+ Room.Host.Value = new User { Username = "peppy", Id = 2 };
+ Room.EndDate.Value = DateTimeOffset.Now.AddMonths(1);
+ Room.Status.Value = new RoomStatusOpen();
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index fe14a1ff0a..b5d946d049 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -121,10 +121,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
room.Playlist.Add(new PlaylistItem
{
- Ruleset = ruleset,
- Beatmap = new BeatmapInfo
+ Ruleset = { Value = ruleset },
+ Beatmap =
{
- Metadata = new BeatmapMetadata()
+ Value = new BeatmapInfo
+ {
+ Metadata = new BeatmapMetadata()
+ }
}
});
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs
new file mode 100644
index 0000000000..24d9f5ab12
--- /dev/null
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs
@@ -0,0 +1,56 @@
+// 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;
+using osu.Game.Beatmaps;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Screens.Multi.Components;
+using osu.Game.Tests.Beatmaps;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Multiplayer
+{
+ public class TestSceneMatchBeatmapDetailArea : MultiplayerTestScene
+ {
+ [Resolved]
+ private BeatmapManager beatmapManager { get; set; }
+
+ [Resolved]
+ private RulesetStore rulesetStore { get; set; }
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ Room.Playlist.Clear();
+
+ Child = new MatchBeatmapDetailArea
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(500),
+ CreateNewItem = createNewItem
+ };
+ });
+
+ private void createNewItem()
+ {
+ Room.Playlist.Add(new PlaylistItem
+ {
+ ID = Room.Playlist.Count,
+ Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
+ Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RequiredMods =
+ {
+ new OsuModHardRock(),
+ new OsuModDoubleTime(),
+ new OsuModAutoplay()
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs
deleted file mode 100644
index 1e3e06ce7a..0000000000
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs
+++ /dev/null
@@ -1,53 +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 osu.Game.Beatmaps;
-using osu.Game.Online.Multiplayer;
-using osu.Game.Screens.Multi.Match.Components;
-using osu.Framework.Graphics;
-using osu.Framework.Utils;
-using osu.Game.Audio;
-using osu.Framework.Allocation;
-
-namespace osu.Game.Tests.Visual.Multiplayer
-{
- [Cached(typeof(IPreviewTrackOwner))]
- public class TestSceneMatchBeatmapPanel : MultiplayerTestScene, IPreviewTrackOwner
- {
- public override IReadOnlyList RequiredTypes => new[]
- {
- typeof(MatchBeatmapPanel)
- };
-
- [Resolved]
- private PreviewTrackManager previewTrackManager { get; set; }
-
- public TestSceneMatchBeatmapPanel()
- {
- Add(new MatchBeatmapPanel
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- });
-
- Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1763072 } });
- Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2101557 } });
- Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1973466 } });
- Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2109801 } });
- Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1922035 } });
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- AddStep("Select random beatmap", () =>
- {
- Room.CurrentItem.Value = Room.Playlist[RNG.Next(Room.Playlist.Count)];
- previewTrackManager.StopAnyPlaying(this);
- });
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs
index e42042f2ea..cf40995fc0 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs
@@ -5,10 +5,10 @@ using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
-using osu.Game.Online.Multiplayer.GameTypes;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Multi.Match.Components;
+using osu.Game.Users;
namespace osu.Game.Tests.Visual.Multiplayer
{
@@ -23,16 +23,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
Room.Playlist.Add(new PlaylistItem
{
- Beatmap = new BeatmapInfo
+ Beatmap =
{
- Metadata = new BeatmapMetadata
+ Value = new BeatmapInfo
{
- Title = "Title",
- Artist = "Artist",
- AuthorString = "Author",
- },
- Version = "Version",
- Ruleset = new OsuRuleset().RulesetInfo
+ Metadata = new BeatmapMetadata
+ {
+ Title = "Title",
+ Artist = "Artist",
+ AuthorString = "Author",
+ },
+ Version = "Version",
+ Ruleset = new OsuRuleset().RulesetInfo
+ }
},
RequiredMods =
{
@@ -42,7 +45,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
});
- Room.Type.Value = new GameTypeTimeshift();
+ Room.Name.Value = "A very awesome room";
+ Room.Host.Value = new User { Id = 2, Username = "peppy" };
Child = new Header();
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHostInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHostInfo.cs
deleted file mode 100644
index 808a45cdf0..0000000000
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHostInfo.cs
+++ /dev/null
@@ -1,35 +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 osu.Framework.Bindables;
-using osu.Framework.Graphics;
-using osu.Game.Screens.Multi.Match.Components;
-using osu.Game.Users;
-
-namespace osu.Game.Tests.Visual.Multiplayer
-{
- public class TestSceneMatchHostInfo : OsuTestScene
- {
- public override IReadOnlyList RequiredTypes => new[]
- {
- typeof(HostInfo)
- };
-
- private readonly Bindable host = new Bindable(new User { Username = "SomeHost" });
-
- public TestSceneMatchHostInfo()
- {
- HostInfo hostInfo;
-
- Child = hostInfo = new HostInfo
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre
- };
-
- hostInfo.Host.BindTo(host);
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs
deleted file mode 100644
index a6c036a876..0000000000
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs
+++ /dev/null
@@ -1,78 +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 NUnit.Framework;
-using osu.Framework.Allocation;
-using osu.Game.Beatmaps;
-using osu.Game.Online.Multiplayer;
-using osu.Game.Online.Multiplayer.RoomStatuses;
-using osu.Game.Rulesets;
-using osu.Game.Screens.Multi.Match.Components;
-
-namespace osu.Game.Tests.Visual.Multiplayer
-{
- [TestFixture]
- public class TestSceneMatchInfo : MultiplayerTestScene
- {
- public override IReadOnlyList RequiredTypes => new[]
- {
- typeof(Info),
- typeof(HeaderButton),
- typeof(ReadyButton),
- typeof(MatchBeatmapPanel)
- };
-
- [BackgroundDependencyLoader]
- private void load(RulesetStore rulesets)
- {
- Add(new Info());
-
- AddStep(@"set name", () => Room.Name.Value = @"Room Name?");
- AddStep(@"set availability", () => Room.Availability.Value = RoomAvailability.FriendsOnly);
- AddStep(@"set status", () => Room.Status.Value = new RoomStatusPlaying());
- AddStep(@"set beatmap", () =>
- {
- Room.Playlist.Clear();
- Room.Playlist.Add(new PlaylistItem
- {
- Beatmap = new BeatmapInfo
- {
- StarDifficulty = 2.4,
- Ruleset = rulesets.GetRuleset(0),
- Metadata = new BeatmapMetadata
- {
- Title = @"My Song",
- Artist = @"VisualTests",
- AuthorString = @"osu!lazer",
- },
- }
- });
- });
-
- AddStep(@"change name", () => Room.Name.Value = @"Room Name!");
- AddStep(@"change availability", () => Room.Availability.Value = RoomAvailability.InviteOnly);
- AddStep(@"change status", () => Room.Status.Value = new RoomStatusOpen());
- AddStep(@"null beatmap", () => Room.Playlist.Clear());
- AddStep(@"change beatmap", () =>
- {
- Room.Playlist.Clear();
- Room.Playlist.Add(new PlaylistItem
- {
- Beatmap = new BeatmapInfo
- {
- StarDifficulty = 4.2,
- Ruleset = rulesets.GetRuleset(3),
- Metadata = new BeatmapMetadata
- {
- Title = @"Your Song",
- Artist = @"Tester",
- AuthorString = @"Someone",
- },
- }
- });
- });
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboardChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboardChatDisplay.cs
new file mode 100644
index 0000000000..e46386b263
--- /dev/null
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboardChatDisplay.cs
@@ -0,0 +1,39 @@
+// 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.Screens.Multi.Match.Components;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Multiplayer
+{
+ public class TestSceneMatchLeaderboardChatDisplay : MultiplayerTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(LeaderboardChatDisplay)
+ };
+
+ protected override bool UseOnlineAPI => true;
+
+ public TestSceneMatchLeaderboardChatDisplay()
+ {
+ Room.RoomID.Value = 7;
+
+ Add(new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(500),
+ Child = new LeaderboardChatDisplay
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchParticipants.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchParticipants.cs
deleted file mode 100644
index 1ac914e27d..0000000000
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchParticipants.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using NUnit.Framework;
-using osu.Framework.Graphics;
-using osu.Game.Screens.Multi.Match.Components;
-using osu.Game.Users;
-
-namespace osu.Game.Tests.Visual.Multiplayer
-{
- [TestFixture]
- public class TestSceneMatchParticipants : MultiplayerTestScene
- {
- public TestSceneMatchParticipants()
- {
- Add(new Participants { RelativeSizeAxes = Axes.Both });
-
- AddStep(@"set max to null", () => Room.MaxParticipants.Value = null);
- AddStep(@"set users", () => Room.Participants.Value = new[]
- {
- new User
- {
- Username = @"Feppla",
- Id = 4271601,
- Country = new Country { FlagName = @"SE" },
- CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
- IsSupporter = true,
- },
- new User
- {
- Username = @"Xilver",
- Id = 3099689,
- Country = new Country { FlagName = @"IL" },
- CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
- IsSupporter = true,
- },
- new User
- {
- Username = @"Wucki",
- Id = 5287410,
- Country = new Country { FlagName = @"FI" },
- CoverUrl = @"https://assets.ppy.sh/user-profile-covers/5287410/5cfeaa9dd41cbce038ecdc9d781396ed4b0108089170bf7f50492ef8eadeb368.jpeg",
- IsSupporter = true,
- },
- });
-
- AddStep(@"set max", () => Room.MaxParticipants.Value = 10);
- AddStep(@"clear users", () => Room.Participants.Value = System.Array.Empty());
- AddStep(@"set max to null", () => Room.MaxParticipants.Value = null);
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs
index 8d842fc865..047e9d860d 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs
@@ -56,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 = CreateBeatmap(Ruleset.Value).BeatmapInfo }));
+ AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } }));
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
AddStep("clear name", () => Room.Name.Value = "");
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs
new file mode 100644
index 0000000000..2c6f34d8a6
--- /dev/null
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.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.IO;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Extensions;
+using osu.Framework.Platform;
+using osu.Framework.Screens;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.Multi.Components;
+using osu.Game.Screens.Select;
+
+namespace osu.Game.Tests.Visual.Multiplayer
+{
+ public class TestSceneMatchSongSelect : MultiplayerTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(MatchSongSelect),
+ typeof(MatchBeatmapDetailArea),
+ };
+
+ [Resolved]
+ private BeatmapManager beatmapManager { get; set; }
+
+ private BeatmapManager manager;
+
+ private RulesetStore rulesets;
+
+ private TestMatchSongSelect songSelect;
+
+ [BackgroundDependencyLoader]
+ private void load(GameHost host, AudioManager audio)
+ {
+ Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
+ Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
+
+ var beatmaps = new List();
+
+ for (int i = 0; i < 6; i++)
+ {
+ int beatmapId = 10 * 10 + i;
+
+ int length = RNG.Next(30000, 200000);
+ double bpm = RNG.NextSingle(80, 200);
+
+ beatmaps.Add(new BeatmapInfo
+ {
+ Ruleset = new OsuRuleset().RulesetInfo,
+ OnlineBeatmapID = beatmapId,
+ Path = "normal.osu",
+ Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
+ Length = length,
+ BPM = bpm,
+ BaseDifficulty = new BeatmapDifficulty
+ {
+ OverallDifficulty = 3.5f,
+ },
+ });
+ }
+
+ manager.Import(new BeatmapSetInfo
+ {
+ OnlineBeatmapSetID = 10,
+ Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
+ Metadata = new BeatmapMetadata
+ {
+ // Create random metadata, then we can check if sorting works based on these
+ Artist = "Some Artist " + RNG.Next(0, 9),
+ Title = $"Some Song (set id 10), max bpm {beatmaps.Max(b => b.BPM):0.#})",
+ AuthorString = "Some Guy " + RNG.Next(0, 9),
+ },
+ Beatmaps = beatmaps,
+ DateAdded = DateTimeOffset.UtcNow,
+ }).Wait();
+ }
+
+ public override void SetUpSteps()
+ {
+ base.SetUpSteps();
+
+ AddStep("reset", () =>
+ {
+ Ruleset.Value = new OsuRuleset().RulesetInfo;
+ Beatmap.SetDefault();
+ });
+
+ AddStep("create song select", () => LoadScreen(songSelect = new TestMatchSongSelect()));
+ AddUntilStep("wait for present", () => songSelect.IsCurrentScreen());
+ }
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ Room.Playlist.Clear();
+ });
+
+ [Test]
+ public void TestItemAddedIfEmptyOnStart()
+ {
+ AddStep("finalise selection", () => songSelect.FinaliseSelection());
+ AddAssert("playlist has 1 item", () => Room.Playlist.Count == 1);
+ }
+
+ [Test]
+ public void TestItemAddedWhenCreateNewItemClicked()
+ {
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddAssert("playlist has 1 item", () => Room.Playlist.Count == 1);
+ }
+
+ [Test]
+ public void TestItemNotAddedIfExistingOnStart()
+ {
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("finalise selection", () => songSelect.FinaliseSelection());
+ AddAssert("playlist has 1 item", () => Room.Playlist.Count == 1);
+ }
+
+ [Test]
+ public void TestAddSameItemMultipleTimes()
+ {
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddAssert("playlist has 2 items", () => Room.Playlist.Count == 2);
+ }
+
+ [Test]
+ public void TestAddItemAfterRearrangement()
+ {
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("rearrange", () =>
+ {
+ var item = Room.Playlist[0];
+ Room.Playlist.RemoveAt(0);
+ Room.Playlist.Add(item);
+ });
+
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddAssert("new item has id 2", () => Room.Playlist.Last().ID == 2);
+ }
+
+ private class TestMatchSongSelect : MatchSongSelect
+ {
+ public new MatchBeatmapDetailArea BeatmapDetails => (MatchBeatmapDetailArea)base.BeatmapDetails;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs
new file mode 100644
index 0000000000..7f79e306ad
--- /dev/null
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs
@@ -0,0 +1,121 @@
+// 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.Bindables;
+using osu.Framework.Screens;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.Multi;
+using osu.Game.Screens.Multi.Match;
+using osu.Game.Screens.Multi.Match.Components;
+using osu.Game.Tests.Beatmaps;
+using osu.Game.Users;
+using osuTK.Input;
+using Header = osu.Game.Screens.Multi.Match.Components.Header;
+
+namespace osu.Game.Tests.Visual.Multiplayer
+{
+ public class TestSceneMatchSubScreen : MultiplayerTestScene
+ {
+ protected override bool UseOnlineAPI => true;
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(Screens.Multi.Multiplayer),
+ typeof(MatchSubScreen),
+ typeof(Header),
+ typeof(Footer)
+ };
+
+ [Cached(typeof(IRoomManager))]
+ private readonly TestRoomManager roomManager = new TestRoomManager();
+
+ [Resolved]
+ private BeatmapManager beatmaps { get; set; }
+
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
+ private TestMatchSubScreen match;
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ Room.CopyFrom(new Room());
+ });
+
+ [SetUpSteps]
+ public void SetupSteps()
+ {
+ AddStep("load match", () => LoadScreen(match = new TestMatchSubScreen(Room)));
+ AddUntilStep("wait for load", () => match.IsCurrentScreen());
+ }
+
+ [Test]
+ public void TestPlaylistItemSelectedOnCreate()
+ {
+ AddStep("set room properties", () =>
+ {
+ Room.Name.Value = "my awesome room";
+ Room.Host.Value = new User { Id = 2, Username = "peppy" };
+ Room.Playlist.Add(new PlaylistItem
+ {
+ Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
+ Ruleset = { Value = new OsuRuleset().RulesetInfo }
+ });
+ });
+
+ AddStep("move mouse to create button", () =>
+ {
+ var footer = match.ChildrenOfType
-
+
diff --git a/osu.Game.Tournament/Components/ControlPanel.cs b/osu.Game.Tournament/Components/ControlPanel.cs
index a9bb1bf42f..fa5c941f1a 100644
--- a/osu.Game.Tournament/Components/ControlPanel.cs
+++ b/osu.Game.Tournament/Components/ControlPanel.cs
@@ -5,7 +5,6 @@ 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;
@@ -35,7 +34,7 @@ namespace osu.Game.Tournament.Components
RelativeSizeAxes = Axes.Both,
Colour = new Color4(54, 54, 54, 255)
},
- new OsuSpriteText
+ new TournamentSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
diff --git a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs
index 361bd92770..99116d4a17 100644
--- a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs
+++ b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs
@@ -9,7 +9,6 @@ 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
@@ -19,7 +18,7 @@ namespace osu.Game.Tournament.Components
public readonly TournamentTeam Team;
protected readonly Sprite Flag;
- protected readonly OsuSpriteText AcronymText;
+ protected readonly TournamentSpriteText AcronymText;
[UsedImplicitly]
private Bindable acronym;
@@ -37,9 +36,9 @@ namespace osu.Game.Tournament.Components
FillMode = FillMode.Fit
};
- AcronymText = new OsuSpriteText
+ AcronymText = new TournamentSpriteText
{
- Font = OsuFont.GetFont(weight: FontWeight.Regular),
+ Font = OsuFont.Torus.With(weight: FontWeight.Regular),
};
}
diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs
index 8a46da9565..48ea36a8f3 100644
--- a/osu.Game.Tournament/Components/SongBar.cs
+++ b/osu.Game.Tournament/Components/SongBar.cs
@@ -13,7 +13,6 @@ 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.Rulesets;
using osu.Game.Screens.Menu;
using osuTK;
@@ -262,7 +261,7 @@ namespace osu.Game.Tournament.Components
static void cp(SpriteText s, Color4 colour)
{
s.Colour = colour;
- s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15);
+ s.Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 15);
}
for (var i = 0; i < tuples.Length; i++)
@@ -278,9 +277,9 @@ namespace osu.Game.Tournament.Components
});
}
- AddText(new OsuSpriteText { Text = heading }, s => cp(s, OsuColour.Gray(0.33f)));
+ AddText(new TournamentSpriteText { Text = heading }, s => cp(s, OsuColour.Gray(0.33f)));
AddText(" ", s => cp(s, OsuColour.Gray(0.33f)));
- AddText(new OsuSpriteText { Text = content }, s => cp(s, OsuColour.Gray(0.5f)));
+ AddText(new TournamentSpriteText { Text = content }, s => cp(s, OsuColour.Gray(0.5f)));
}
}
}
diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs
index 51483a0964..394ffe304e 100644
--- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs
+++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs
@@ -15,7 +15,6 @@ 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;
@@ -77,14 +76,14 @@ namespace osu.Game.Tournament.Components
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- new OsuSpriteText
+ new TournamentSpriteText
{
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),
+ Font = OsuFont.Torus.With(weight: FontWeight.Bold),
},
new FillFlowContainer
{
@@ -95,28 +94,28 @@ namespace osu.Game.Tournament.Components
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
- new OsuSpriteText
+ new TournamentSpriteText
{
Text = "mapper",
Padding = new MarginPadding { Right = 5 },
- Font = OsuFont.GetFont(italics: true, weight: FontWeight.Regular, size: 14)
+ Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: 14)
},
- new OsuSpriteText
+ new TournamentSpriteText
{
Text = Beatmap.Metadata.AuthorString,
Padding = new MarginPadding { Right = 20 },
- Font = OsuFont.GetFont(italics: true, weight: FontWeight.Bold, size: 14)
+ Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
},
- new OsuSpriteText
+ new TournamentSpriteText
{
Text = "difficulty",
Padding = new MarginPadding { Right = 5 },
- Font = OsuFont.GetFont(italics: true, weight: FontWeight.Regular, size: 14)
+ Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: 14)
},
- new OsuSpriteText
+ new TournamentSpriteText
{
Text = Beatmap.Version,
- Font = OsuFont.GetFont(italics: true, weight: FontWeight.Bold, size: 14)
+ Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
},
}
}
diff --git a/osu.Game.Tournament/Models/SeedingBeatmap.cs b/osu.Game.Tournament/Models/SeedingBeatmap.cs
new file mode 100644
index 0000000000..2cd6fa7188
--- /dev/null
+++ b/osu.Game.Tournament/Models/SeedingBeatmap.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.Bindables;
+using osu.Game.Beatmaps;
+
+namespace osu.Game.Tournament.Models
+{
+ public class SeedingBeatmap
+ {
+ public int ID;
+
+ public BeatmapInfo BeatmapInfo;
+
+ public long Score;
+
+ public Bindable Seed = new BindableInt
+ {
+ MinValue = 1,
+ MaxValue = 64
+ };
+ }
+}
diff --git a/osu.Game.Tournament/Models/SeedingResult.cs b/osu.Game.Tournament/Models/SeedingResult.cs
new file mode 100644
index 0000000000..87aaf8bf36
--- /dev/null
+++ b/osu.Game.Tournament/Models/SeedingResult.cs
@@ -0,0 +1,21 @@
+// 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 osu.Framework.Bindables;
+
+namespace osu.Game.Tournament.Models
+{
+ public class SeedingResult
+ {
+ public List Beatmaps = new List();
+
+ public Bindable Mod = new Bindable();
+
+ public Bindable Seed = new BindableInt
+ {
+ MinValue = 1,
+ MaxValue = 64
+ };
+ }
+}
diff --git a/osu.Game.Tournament/Models/TournamentTeam.cs b/osu.Game.Tournament/Models/TournamentTeam.cs
index 54b8a35180..7fca75cea4 100644
--- a/osu.Game.Tournament/Models/TournamentTeam.cs
+++ b/osu.Game.Tournament/Models/TournamentTeam.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.Users;
@@ -29,6 +30,32 @@ namespace osu.Game.Tournament.Models
///
public Bindable Acronym = new Bindable(string.Empty);
+ public BindableList SeedingResults = new BindableList();
+
+ public double AverageRank
+ {
+ get
+ {
+ var ranks = Players.Select(p => p.Statistics?.Ranks.Global)
+ .Where(i => i.HasValue)
+ .Select(i => i.Value)
+ .ToArray();
+
+ if (ranks.Length == 0)
+ return 0;
+
+ return ranks.Average();
+ }
+ }
+
+ public Bindable Seed = new Bindable(string.Empty);
+
+ public Bindable LastYearPlacing = new BindableInt
+ {
+ MinValue = 1,
+ MaxValue = 64
+ };
+
[JsonProperty]
public BindableList Players { get; set; } = new BindableList();
diff --git a/osu.Game.Tournament/Resources/Fonts/Aquatico-Light.bin b/osu.Game.Tournament/Resources/Fonts/Aquatico-Light.bin
deleted file mode 100644
index 42cfdf08de..0000000000
Binary files a/osu.Game.Tournament/Resources/Fonts/Aquatico-Light.bin and /dev/null differ
diff --git a/osu.Game.Tournament/Resources/Fonts/Aquatico-Light_0.png b/osu.Game.Tournament/Resources/Fonts/Aquatico-Light_0.png
deleted file mode 100644
index 332d9ca056..0000000000
Binary files a/osu.Game.Tournament/Resources/Fonts/Aquatico-Light_0.png and /dev/null differ
diff --git a/osu.Game.Tournament/Resources/Fonts/Aquatico-Regular.bin b/osu.Game.Tournament/Resources/Fonts/Aquatico-Regular.bin
deleted file mode 100644
index 3047c2eb3e..0000000000
Binary files a/osu.Game.Tournament/Resources/Fonts/Aquatico-Regular.bin and /dev/null differ
diff --git a/osu.Game.Tournament/Resources/Fonts/Aquatico-Regular_0.png b/osu.Game.Tournament/Resources/Fonts/Aquatico-Regular_0.png
deleted file mode 100644
index 1252d233d3..0000000000
Binary files a/osu.Game.Tournament/Resources/Fonts/Aquatico-Regular_0.png and /dev/null differ
diff --git a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs
index 549ff26018..4126f2db65 100644
--- a/osu.Game.Tournament/Screens/Drawings/Components/Group.cs
+++ b/osu.Game.Tournament/Screens/Drawings/Components/Group.cs
@@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osuTK;
@@ -43,7 +42,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
Colour = new Color4(54, 54, 54, 255)
},
// Group name
- new OsuSpriteText
+ new TournamentSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@@ -51,7 +50,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
Position = new Vector2(0, 7f),
Text = $"GROUP {name.ToUpperInvariant()}",
- Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 8),
+ Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 8),
Colour = new Color4(255, 204, 34, 255),
},
teams = new FillFlowContainer
@@ -134,7 +133,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
AcronymText.Anchor = Anchor.TopCentre;
AcronymText.Origin = Anchor.TopCentre;
AcronymText.Text = team.Acronym.Value.ToUpperInvariant();
- AcronymText.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 10);
+ AcronymText.Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 10);
InternalChildren = new Drawable[]
{
diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs
index 5efa0a1e69..8be66ff98c 100644
--- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs
+++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs
@@ -14,7 +14,6 @@ using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Drawings.Components;
@@ -29,7 +28,7 @@ namespace osu.Game.Tournament.Screens.Drawings
private ScrollingTeamContainer teamsContainer;
private GroupContainer groupsContainer;
- private OsuSpriteText fullTeamNameText;
+ private TournamentSpriteText fullTeamNameText;
private readonly List allTeams = new List();
@@ -109,18 +108,18 @@ namespace osu.Game.Tournament.Screens.Drawings
RelativeSizeAxes = Axes.X,
},
// Scrolling team name
- fullTeamNameText = new OsuSpriteText
+ fullTeamNameText = new TournamentSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, 45f),
- Colour = OsuColour.Gray(0.33f),
+ Colour = OsuColour.Gray(0.95f),
Alpha = 0,
- Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42),
+ Font = OsuFont.Torus.With(weight: FontWeight.Light, size: 42),
}
}
},
diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs
new file mode 100644
index 0000000000..e68946aaf2
--- /dev/null
+++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs
@@ -0,0 +1,288 @@
+// 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.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Overlays.Settings;
+using osu.Game.Rulesets;
+using osu.Game.Tournament.Components;
+using osu.Game.Tournament.Models;
+using osuTK;
+
+namespace osu.Game.Tournament.Screens.Editors
+{
+ public class SeedingEditorScreen : TournamentEditorScreen
+ {
+ private readonly TournamentTeam team;
+
+ protected override BindableList Storage => team.SeedingResults;
+
+ public SeedingEditorScreen(TournamentTeam team)
+ {
+ this.team = team;
+ }
+
+ public class SeeingResultRow : CompositeDrawable, IModelBacked
+ {
+ public SeedingResult Model { get; }
+
+ [Resolved]
+ private LadderInfo ladderInfo { get; set; }
+
+ public SeeingResultRow(TournamentTeam team, SeedingResult round)
+ {
+ Model = round;
+
+ Masking = true;
+ CornerRadius = 10;
+
+ SeedingBeatmapEditor beatmapEditor = new SeedingBeatmapEditor(round)
+ {
+ Width = 0.95f
+ };
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ Colour = OsuColour.Gray(0.1f),
+ RelativeSizeAxes = Axes.Both,
+ },
+ new FillFlowContainer
+ {
+ Margin = new MarginPadding(5),
+ Padding = new MarginPadding { Right = 160 },
+ Spacing = new Vector2(5),
+ Direction = FillDirection.Full,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ new SettingsTextBox
+ {
+ LabelText = "Mod",
+ Width = 0.33f,
+ Bindable = Model.Mod
+ },
+ new SettingsSlider
+ {
+ LabelText = "Seed",
+ Width = 0.33f,
+ Bindable = Model.Seed
+ },
+ new SettingsButton
+ {
+ Width = 0.2f,
+ Margin = new MarginPadding(10),
+ Text = "Add beatmap",
+ Action = () => beatmapEditor.CreateNew()
+ },
+ beatmapEditor
+ }
+ },
+ new DangerousSettingsButton
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ RelativeSizeAxes = Axes.None,
+ Width = 150,
+ Text = "Delete result",
+ Action = () =>
+ {
+ Expire();
+ team.SeedingResults.Remove(Model);
+ },
+ }
+ };
+
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ }
+
+ public class SeedingBeatmapEditor : CompositeDrawable
+ {
+ private readonly SeedingResult round;
+ private readonly FillFlowContainer flow;
+
+ public SeedingBeatmapEditor(SeedingResult round)
+ {
+ this.round = round;
+
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ InternalChild = flow = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ LayoutDuration = 200,
+ LayoutEasing = Easing.OutQuint,
+ ChildrenEnumerable = round.Beatmaps.Select(p => new SeedingBeatmapRow(round, p))
+ };
+ }
+
+ public void CreateNew()
+ {
+ var user = new SeedingBeatmap();
+ round.Beatmaps.Add(user);
+ flow.Add(new SeedingBeatmapRow(round, user));
+ }
+
+ public class SeedingBeatmapRow : CompositeDrawable
+ {
+ private readonly SeedingResult result;
+ public SeedingBeatmap Model { get; }
+
+ [Resolved]
+ protected IAPIProvider API { get; private set; }
+
+ private readonly Bindable beatmapId = new Bindable();
+
+ private readonly Bindable score = new Bindable();
+
+ private readonly Container drawableContainer;
+
+ public SeedingBeatmapRow(SeedingResult result, SeedingBeatmap beatmap)
+ {
+ this.result = result;
+ Model = beatmap;
+
+ Margin = new MarginPadding(10);
+
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ Masking = true;
+ CornerRadius = 5;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ Colour = OsuColour.Gray(0.2f),
+ RelativeSizeAxes = Axes.Both,
+ },
+ new FillFlowContainer
+ {
+ Margin = new MarginPadding(5),
+ Padding = new MarginPadding { Right = 160 },
+ Spacing = new Vector2(5),
+ Direction = FillDirection.Horizontal,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new SettingsNumberBox
+ {
+ LabelText = "Beatmap ID",
+ RelativeSizeAxes = Axes.None,
+ Width = 200,
+ Bindable = beatmapId,
+ },
+ new SettingsSlider
+ {
+ LabelText = "Seed",
+ RelativeSizeAxes = Axes.None,
+ Width = 200,
+ Bindable = beatmap.Seed
+ },
+ new SettingsTextBox
+ {
+ LabelText = "Score",
+ RelativeSizeAxes = Axes.None,
+ Width = 200,
+ Bindable = score,
+ },
+ drawableContainer = new Container
+ {
+ Size = new Vector2(100, 70),
+ },
+ }
+ },
+ new DangerousSettingsButton
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ RelativeSizeAxes = Axes.None,
+ Width = 150,
+ Text = "Delete Beatmap",
+ Action = () =>
+ {
+ Expire();
+ result.Beatmaps.Remove(beatmap);
+ },
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ beatmapId.Value = Model.ID.ToString();
+ beatmapId.BindValueChanged(idString =>
+ {
+ int parsed;
+
+ int.TryParse(idString.NewValue, out parsed);
+
+ Model.ID = parsed;
+
+ if (idString.NewValue != idString.OldValue)
+ Model.BeatmapInfo = null;
+
+ if (Model.BeatmapInfo != null)
+ {
+ updatePanel();
+ return;
+ }
+
+ var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = Model.ID });
+
+ req.Success += res =>
+ {
+ Model.BeatmapInfo = res.ToBeatmap(rulesets);
+ updatePanel();
+ };
+
+ req.Failure += _ =>
+ {
+ Model.BeatmapInfo = null;
+ updatePanel();
+ };
+
+ API.Queue(req);
+ }, true);
+
+ score.Value = Model.Score.ToString();
+ score.BindValueChanged(str => long.TryParse(str.NewValue, out Model.Score));
+ }
+
+ private void updatePanel()
+ {
+ drawableContainer.Clear();
+
+ if (Model.BeatmapInfo != null)
+ {
+ drawableContainer.Child = new TournamentBeatmapPanel(Model.BeatmapInfo, result.Mod.Value)
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Width = 300
+ };
+ }
+ }
+ }
+ }
+ }
+
+ protected override SeeingResultRow CreateDrawable(SeedingResult model) => new SeeingResultRow(team, model);
+ }
+}
diff --git a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
index 494dd73edd..ca8bce1cca 100644
--- a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
+++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
@@ -57,6 +57,9 @@ namespace osu.Game.Tournament.Screens.Editors
private readonly Container drawableContainer;
+ [Resolved(canBeNull: true)]
+ private TournamentSceneManager sceneManager { get; set; }
+
[Resolved]
private LadderInfo ladderInfo { get; set; }
@@ -113,6 +116,18 @@ namespace osu.Game.Tournament.Screens.Editors
Width = 0.2f,
Bindable = Model.FlagName
},
+ new SettingsTextBox
+ {
+ LabelText = "Seed",
+ Width = 0.2f,
+ Bindable = Model.Seed
+ },
+ new SettingsSlider
+ {
+ LabelText = "Last Year Placement",
+ Width = 0.33f,
+ Bindable = Model.LastYearPlacing
+ },
new SettingsButton
{
Width = 0.11f,
@@ -131,7 +146,17 @@ namespace osu.Game.Tournament.Screens.Editors
ladderInfo.Teams.Remove(Model);
},
},
- playerEditor
+ playerEditor,
+ new SettingsButton
+ {
+ Width = 0.2f,
+ Margin = new MarginPadding(10),
+ Text = "Edit seeding results",
+ Action = () =>
+ {
+ sceneManager?.SetScreen(new SeedingEditorScreen(team));
+ }
+ },
}
},
};
diff --git a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs
index 32cf6bbcc8..5598910824 100644
--- a/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs
+++ b/osu.Game.Tournament/Screens/Editors/TournamentEditorScreen.cs
@@ -1,6 +1,8 @@
// 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.Specialized;
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
@@ -71,8 +73,19 @@ namespace osu.Game.Tournament.Screens.Editors
}
});
- Storage.ItemsAdded += items => items.ForEach(i => flow.Add(CreateDrawable(i)));
- Storage.ItemsRemoved += items => items.ForEach(i => flow.RemoveAll(d => d.Model == i));
+ Storage.CollectionChanged += (_, args) =>
+ {
+ switch (args.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ args.NewItems.Cast().ForEach(i => flow.Add(CreateDrawable(i)));
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ args.OldItems.Cast().ForEach(i => flow.RemoveAll(d => d.Model == i));
+ break;
+ }
+ };
foreach (var model in Storage)
flow.Add(CreateDrawable(model));
diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs
index 9e1888b44b..ce17c392d0 100644
--- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchHeader.cs
@@ -5,9 +5,9 @@ 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.Input.Events;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
@@ -30,7 +30,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
new TournamentLogo(),
new RoundDisplay
{
- Y = 10,
+ Y = 5,
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre,
},
@@ -51,9 +51,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{
private readonly TeamColour teamColour;
- private readonly Color4 red = new Color4(129, 68, 65, 255);
- private readonly Color4 blue = new Color4(41, 91, 97, 255);
-
private readonly Bindable currentMatch = new Bindable();
private readonly Bindable currentTeam = new Bindable();
private readonly Bindable currentTeamScore = new Bindable();
@@ -106,7 +103,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
private void teamChanged(TournamentTeam team)
{
- var colour = teamColour == TeamColour.Red ? red : blue;
+ var colour = teamColour == TeamColour.Red ? TournamentGame.COLOUR_RED : TournamentGame.COLOUR_BLUE;
var flip = teamColour != TeamColour.Red;
InternalChildren = new Drawable[]
@@ -169,13 +166,13 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
Children = new Drawable[]
{
Flag,
- new OsuSpriteText
+ new TournamentSpriteText
{
Text = team?.FullName.Value.ToUpper() ?? "???",
X = (flip ? -1 : 1) * 90,
Y = -10,
Colour = colour,
- Font = TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 20),
+ Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: 20),
Origin = anchor,
Anchor = anchor,
},
@@ -188,10 +185,31 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
{
private readonly Bindable currentMatch = new Bindable();
+ private readonly TournamentSpriteText text;
+
public RoundDisplay()
{
Width = 200;
Height = 20;
+
+ Masking = true;
+ CornerRadius = 10;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ Colour = OsuColour.Gray(0.18f),
+ RelativeSizeAxes = Axes.Both,
+ },
+ text = new TournamentSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Colour = Color4.White,
+ Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: 16),
+ },
+ };
}
[BackgroundDependencyLoader]
@@ -201,20 +219,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
currentMatch.BindTo(ladder.CurrentMatch);
}
- private void matchChanged(ValueChangedEvent match)
- {
- InternalChildren = new Drawable[]
- {
- new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Colour = Color4.White,
- Text = match.NewValue.Round.Value?.Name.Value ?? "Unknown Round",
- Font = TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 18),
- },
- };
- }
+ private void matchChanged(ValueChangedEvent match) =>
+ text.Text = match.NewValue.Round.Value?.Name.Value ?? "Unknown Round";
}
}
}
diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs
index cc7903f2fa..fcf1469278 100644
--- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs
+++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs
@@ -123,8 +123,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
public bool Winning
{
set => DisplayedCountSpriteText.Font = value
- ? TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Regular, size: 60)
- : TournamentFont.GetFont(typeface: TournamentTypeface.Aquatico, weight: FontWeight.Light, size: 40);
+ ? OsuFont.Torus.With(weight: FontWeight.Regular, size: 60)
+ : OsuFont.Torus.With(weight: FontWeight.Light, size: 40);
}
}
}
diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs
index 031d6bf3d2..88d7b95b0c 100644
--- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs
+++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs
@@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
@@ -26,7 +25,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{
private readonly TournamentMatch match;
private readonly bool losers;
- private OsuSpriteText scoreText;
+ private TournamentSpriteText scoreText;
private Box background;
private readonly Bindable score = new Bindable();
@@ -69,7 +68,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
AcronymText.Anchor = AcronymText.Origin = Anchor.CentreLeft;
AcronymText.Padding = new MarginPadding { Left = 50 };
- AcronymText.Font = OsuFont.GetFont(size: 24);
+ AcronymText.Font = OsuFont.Torus.With(size: 24);
if (match != null)
{
@@ -119,11 +118,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
Alpha = 0.8f,
RelativeSizeAxes = Axes.Both,
},
- scoreText = new OsuSpriteText
+ scoreText = new TournamentSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Font = OsuFont.GetFont(size: 20),
+ Font = OsuFont.Torus.With(size: 20),
}
}
}
@@ -184,7 +183,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
background.FadeColour(winner ? colourWinner : colourNormal, winner ? 500 : 0, Easing.OutQuint);
- scoreText.Font = AcronymText.Font = OsuFont.GetFont(weight: winner ? FontWeight.Bold : FontWeight.Regular);
+ scoreText.Font = AcronymText.Font = OsuFont.Torus.With(weight: winner ? FontWeight.Bold : FontWeight.Regular);
}
public MenuItem[] ContextMenuItems
diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs
index dacd98d3b8..d14ebb4d03 100644
--- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs
+++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentRound.cs
@@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Models;
using osuTK.Graphics;
@@ -22,8 +21,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
public DrawableTournamentRound(TournamentRound round, bool losers = false)
{
- OsuSpriteText textName;
- OsuSpriteText textDescription;
+ TournamentSpriteText textName;
+ TournamentSpriteText textDescription;
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
@@ -32,15 +31,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
- textDescription = new OsuSpriteText
+ textDescription = new TournamentSpriteText
{
Colour = Color4.Black,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre
},
- textName = new OsuSpriteText
+ textName = new TournamentSpriteText
{
- Font = OsuFont.GetFont(weight: FontWeight.Bold),
+ Font = OsuFont.Torus.With(weight: FontWeight.Bold),
Colour = Color4.Black,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre
diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs
index 0864d25a2f..4aea7ff4c0 100644
--- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs
+++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs
@@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using System.Collections.Specialized;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Tournament.Components;
@@ -90,8 +90,19 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
foreach (var r in rounds.Prepend(new TournamentRound()))
add(r);
- rounds.ItemsRemoved += items => items.ForEach(i => Control.RemoveDropdownItem(i));
- rounds.ItemsAdded += items => items.ForEach(add);
+ rounds.CollectionChanged += (_, args) =>
+ {
+ switch (args.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ args.NewItems.Cast().ForEach(add);
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ args.OldItems.Cast().ForEach(i => Control.RemoveDropdownItem(i));
+ break;
+ }
+ };
}
private readonly List refBindables = new List();
@@ -114,55 +125,5 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
});
}
}
-
- private class SettingsTeamDropdown : LadderSettingsDropdown
- {
- public SettingsTeamDropdown(BindableList teams)
- {
- foreach (var t in teams.Prepend(new TournamentTeam()))
- add(t);
-
- teams.ItemsRemoved += items => items.ForEach(i => Control.RemoveDropdownItem(i));
- teams.ItemsAdded += items => items.ForEach(add);
- }
-
- private readonly List refBindables = new List();
-
- private T boundReference(T obj)
- where T : IBindable
- {
- obj = (T)obj.GetBoundCopy();
- refBindables.Add(obj);
- return obj;
- }
-
- private void add(TournamentTeam team)
- {
- Control.AddDropdownItem(team);
- boundReference(team.FullName).BindValueChanged(_ =>
- {
- Control.RemoveDropdownItem(team);
- Control.AddDropdownItem(team);
- });
- }
- }
-
- private class LadderSettingsDropdown : SettingsDropdown
- {
- protected override OsuDropdown CreateDropdown() => new DropdownControl();
-
- private new class DropdownControl : SettingsDropdown.DropdownControl
- {
- protected override DropdownMenu CreateMenu() => new Menu();
-
- private new class Menu : OsuDropdownMenu
- {
- public Menu()
- {
- MaxHeight = 200;
- }
- }
- }
- }
}
}
diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettingsDropdown.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettingsDropdown.cs
new file mode 100644
index 0000000000..347e4d91e0
--- /dev/null
+++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettingsDropdown.cs
@@ -0,0 +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 osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Settings;
+
+namespace osu.Game.Tournament.Screens.Ladder.Components
+{
+ public class LadderSettingsDropdown : SettingsDropdown
+ {
+ protected override OsuDropdown CreateDropdown() => new DropdownControl();
+
+ private new class DropdownControl : SettingsDropdown.DropdownControl
+ {
+ protected override DropdownMenu CreateMenu() => new Menu();
+
+ private new class Menu : OsuDropdownMenu
+ {
+ public Menu()
+ {
+ MaxHeight = 200;
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Screens/Ladder/Components/SettingsTeamDropdown.cs b/osu.Game.Tournament/Screens/Ladder/Components/SettingsTeamDropdown.cs
new file mode 100644
index 0000000000..a630e51e44
--- /dev/null
+++ b/osu.Game.Tournament/Screens/Ladder/Components/SettingsTeamDropdown.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.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Game.Tournament.Models;
+
+namespace osu.Game.Tournament.Screens.Ladder.Components
+{
+ public class SettingsTeamDropdown : LadderSettingsDropdown
+ {
+ public SettingsTeamDropdown(BindableList teams)
+ {
+ foreach (var t in teams.Prepend(new TournamentTeam()))
+ add(t);
+
+ teams.CollectionChanged += (_, args) =>
+ {
+ switch (args.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ args.NewItems.Cast().ForEach(add);
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ args.OldItems.Cast().ForEach(i => Control.RemoveDropdownItem(i));
+ break;
+ }
+ };
+ }
+
+ private readonly List refBindables = new List();
+
+ private T boundReference(T obj)
+ where T : IBindable
+ {
+ obj = (T)obj.GetBoundCopy();
+ refBindables.Add(obj);
+ return obj;
+ }
+
+ private void add(TournamentTeam team)
+ {
+ Control.AddDropdownItem(team);
+ boundReference(team.FullName).BindValueChanged(_ =>
+ {
+ Control.RemoveDropdownItem(team);
+ Control.AddDropdownItem(team);
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs
index 66e68a0f37..293f6e0068 100644
--- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs
+++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.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.Collections.Specialized;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Caching;
@@ -41,7 +42,7 @@ namespace osu.Game.Tournament.Screens.Ladder
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
- new TourneyVideo(storage.GetStream(@"BG Side Logo - OWC.m4v"))
+ new TourneyVideo(storage.GetStream(@"videos/ladder.m4v"))
{
RelativeSizeAxes = Axes.Both,
Loop = true,
@@ -68,22 +69,24 @@ namespace osu.Game.Tournament.Screens.Ladder
foreach (var match in LadderInfo.Matches)
addMatch(match);
- LadderInfo.Rounds.ItemsAdded += _ => layout.Invalidate();
- LadderInfo.Rounds.ItemsRemoved += _ => layout.Invalidate();
-
- LadderInfo.Matches.ItemsAdded += matches =>
+ LadderInfo.Rounds.CollectionChanged += (_, __) => layout.Invalidate();
+ LadderInfo.Matches.CollectionChanged += (_, args) =>
{
- foreach (var p in matches)
- addMatch(p);
- layout.Invalidate();
- };
-
- LadderInfo.Matches.ItemsRemoved += matches =>
- {
- foreach (var p in matches)
+ switch (args.Action)
{
- foreach (var d in MatchesContainer.Where(d => d.Match == p))
- d.Expire();
+ case NotifyCollectionChangedAction.Add:
+ foreach (var p in args.NewItems.Cast())
+ addMatch(p);
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ foreach (var p in args.OldItems.Cast())
+ {
+ foreach (var d in MatchesContainer.Where(d => d.Match == p))
+ d.Expire();
+ }
+
+ break;
}
layout.Invalidate();
diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
index c3875716b8..c42d0a6da3 100644
--- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
+++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
@@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
-using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.IPC;
@@ -56,7 +55,7 @@ namespace osu.Game.Tournament.Screens.MapPool
{
Children = new Drawable[]
{
- new OsuSpriteText
+ new TournamentSpriteText
{
Text = "Current Mode"
},
diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs
index 4b46264055..080570eac4 100644
--- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs
+++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs
@@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Ladder.Components;
@@ -34,7 +33,7 @@ namespace osu.Game.Tournament.Screens.Schedule
InternalChildren = new Drawable[]
{
- new TourneyVideo(storage.GetStream(@"BG Side Logo - OWC.m4v"))
+ new TourneyVideo(storage.GetStream(@"videos/schedule.m4v"))
{
RelativeSizeAxes = Axes.Both,
Loop = true,
@@ -107,20 +106,20 @@ namespace osu.Game.Tournament.Screens.Schedule
Height = 0.25f,
Children = new Drawable[]
{
- new OsuSpriteText
+ new TournamentSpriteText
{
Margin = new MarginPadding { Left = -10, Bottom = 10, Top = -5 },
Spacing = new Vector2(10, 0),
Text = match.NewValue.Round.Value?.Name.Value,
Colour = Color4.Black,
- Font = OsuFont.GetFont(size: 20)
+ Font = OsuFont.Torus.With(size: 20)
},
new ScheduleMatch(match.NewValue, false),
- new OsuSpriteText
+ new TournamentSpriteText
{
Text = "Start Time " + match.NewValue.Date.Value.ToUniversalTime().ToString("HH:mm UTC"),
Colour = Color4.Black,
- Font = OsuFont.GetFont(size: 20)
+ Font = OsuFont.Torus.With(size: 20)
},
}
}
@@ -150,7 +149,7 @@ namespace osu.Game.Tournament.Screens.Schedule
Alpha = conditional ? 0.6f : 1,
Margin = new MarginPadding { Horizontal = 10, Vertical = 5 },
});
- AddInternal(new OsuSpriteText
+ AddInternal(new TournamentSpriteText
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomLeft,
@@ -174,13 +173,13 @@ namespace osu.Game.Tournament.Screens.Schedule
Padding = new MarginPadding { Left = 30, Top = 30 };
InternalChildren = new Drawable[]
{
- new OsuSpriteText
+ new TournamentSpriteText
{
X = 30,
Text = title,
Colour = Color4.Black,
Spacing = new Vector2(10, 0),
- Font = OsuFont.GetFont(size: 30)
+ Font = OsuFont.Torus.With(size: 30)
},
content = new FillFlowContainer
{
diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs
index 8e1481d87c..023582166c 100644
--- a/osu.Game.Tournament/Screens/SetupScreen.cs
+++ b/osu.Game.Tournament/Screens/SetupScreen.cs
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Online.API;
@@ -147,7 +146,7 @@ namespace osu.Game.Tournament.Screens
public Action Action;
- private OsuSpriteText valueText;
+ private TournamentSpriteText valueText;
protected override Drawable CreateComponent() => new Container
{
@@ -155,7 +154,7 @@ namespace osu.Game.Tournament.Screens
RelativeSizeAxes = Axes.X,
Children = new Drawable[]
{
- valueText = new OsuSpriteText
+ valueText = new TournamentSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs
index 20928499bf..d809dfc994 100644
--- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs
+++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs
@@ -10,7 +10,7 @@ namespace osu.Game.Tournament.Screens.Showcase
[BackgroundDependencyLoader]
private void load()
{
- AddInternal(new TournamentLogo(false));
+ AddInternal(new TournamentLogo());
}
}
}
diff --git a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs
index 1fee2b29e8..6ad5ccaf0c 100644
--- a/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs
+++ b/osu.Game.Tournament/Screens/Showcase/TournamentLogo.cs
@@ -11,20 +11,12 @@ namespace osu.Game.Tournament.Screens.Showcase
{
public class TournamentLogo : CompositeDrawable
{
- public TournamentLogo(bool includeRoundBackground = true)
+ public TournamentLogo()
{
RelativeSizeAxes = Axes.X;
Margin = new MarginPadding { Vertical = 5 };
- if (includeRoundBackground)
- {
- AutoSizeAxes = Axes.Y;
- }
- else
- {
- Masking = true;
- Height = 100;
- }
+ Height = 100;
}
[BackgroundDependencyLoader]
@@ -32,9 +24,11 @@ namespace osu.Game.Tournament.Screens.Showcase
{
InternalChild = new Sprite
{
- Texture = textures.Get("game-screen-logo"),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
+ FillMode = FillMode.Fit,
+ RelativeSizeAxes = Axes.Both,
+ Texture = textures.Get("game-screen-logo"),
};
}
}
diff --git a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs
new file mode 100644
index 0000000000..db5363c155
--- /dev/null
+++ b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs
@@ -0,0 +1,316 @@
+// 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.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Platform;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Tournament.Components;
+using osu.Game.Tournament.Models;
+using osu.Game.Tournament.Screens.Ladder.Components;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tournament.Screens.TeamIntro
+{
+ public class SeedingScreen : TournamentScreen, IProvideVideo
+ {
+ private Container mainContainer;
+
+ private readonly Bindable currentMatch = new Bindable();
+
+ private readonly Bindable currentTeam = new Bindable();
+
+ [BackgroundDependencyLoader]
+ private void load(Storage storage)
+ {
+ RelativeSizeAxes = Axes.Both;
+
+ InternalChildren = new Drawable[]
+ {
+ new TourneyVideo(storage.GetStream(@"videos/seeding.m4v"))
+ {
+ RelativeSizeAxes = Axes.Both,
+ Loop = true,
+ },
+ mainContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new ControlPanel
+ {
+ Children = new Drawable[]
+ {
+ new TourneyButton
+ {
+ RelativeSizeAxes = Axes.X,
+ Text = "Show first team",
+ Action = () => currentTeam.Value = currentMatch.Value.Team1.Value,
+ },
+ new TourneyButton
+ {
+ RelativeSizeAxes = Axes.X,
+ Text = "Show second team",
+ Action = () => currentTeam.Value = currentMatch.Value.Team2.Value,
+ },
+ new SettingsTeamDropdown(LadderInfo.Teams)
+ {
+ LabelText = "Show specific team",
+ Bindable = currentTeam,
+ }
+ }
+ }
+ };
+
+ currentMatch.BindValueChanged(matchChanged);
+ currentMatch.BindTo(LadderInfo.CurrentMatch);
+
+ currentTeam.BindValueChanged(teamChanged, true);
+ }
+
+ private void teamChanged(ValueChangedEvent team)
+ {
+ if (team.NewValue == null)
+ {
+ mainContainer.Clear();
+ return;
+ }
+
+ showTeam(team.NewValue);
+ }
+
+ private void matchChanged(ValueChangedEvent match) =>
+ currentTeam.Value = currentMatch.Value.Team1.Value;
+
+ private void showTeam(TournamentTeam team)
+ {
+ mainContainer.Children = new Drawable[]
+ {
+ new LeftInfo(team) { Position = new Vector2(55, 150), },
+ new RightInfo(team) { Position = new Vector2(500, 150), },
+ };
+ }
+
+ private class RightInfo : CompositeDrawable
+ {
+ public RightInfo(TournamentTeam team)
+ {
+ FillFlowContainer fill;
+
+ Width = 400;
+
+ InternalChildren = new Drawable[]
+ {
+ fill = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ },
+ };
+
+ foreach (var seeding in team.SeedingResults)
+ {
+ fill.Add(new ModRow(seeding.Mod.Value, seeding.Seed.Value));
+ foreach (var beatmap in seeding.Beatmaps)
+ fill.Add(new BeatmapScoreRow(beatmap));
+ }
+ }
+
+ private class BeatmapScoreRow : CompositeDrawable
+ {
+ public BeatmapScoreRow(SeedingBeatmap beatmap)
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ InternalChildren = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(5),
+ Children = new Drawable[]
+ {
+ new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Title, Colour = Color4.Black, },
+ new TournamentSpriteText { Text = "by", Colour = Color4.Black, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
+ new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Artist, Colour = Color4.Black, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
+ }
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(40),
+ Children = new Drawable[]
+ {
+ new TournamentSpriteText { Text = beatmap.Score.ToString("#,0"), Colour = Color4.Black, Width = 80 },
+ new TournamentSpriteText { Text = "#" + beatmap.Seed.Value.ToString("#,0"), Colour = Color4.Black, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
+ }
+ },
+ };
+ }
+ }
+
+ private class ModRow : CompositeDrawable
+ {
+ private readonly string mods;
+ private readonly int seeding;
+
+ public ModRow(string mods, int seeding)
+ {
+ this.mods = mods;
+ this.seeding = seeding;
+
+ Padding = new MarginPadding { Vertical = 10 };
+
+ AutoSizeAxes = Axes.Y;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ InternalChildren = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(5),
+ Children = new Drawable[]
+ {
+ new Sprite
+ {
+ Texture = textures.Get($"mods/{mods.ToLower()}"),
+ Scale = new Vector2(0.5f)
+ },
+ new Container
+ {
+ Size = new Vector2(50, 16),
+ CornerRadius = 10,
+ Masking = true,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ new TournamentSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = seeding.ToString("#,0"),
+ },
+ }
+ },
+ }
+ },
+ };
+ }
+ }
+ }
+
+ private class LeftInfo : CompositeDrawable
+ {
+ public LeftInfo(TournamentTeam team)
+ {
+ FillFlowContainer fill;
+
+ Width = 200;
+
+ if (team == null) return;
+
+ InternalChildren = new Drawable[]
+ {
+ fill = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new TeamDisplay(team) { Margin = new MarginPadding { Bottom = 30 } },
+ new RowDisplay("Average Rank:", $"#{team.AverageRank:#,0}"),
+ new RowDisplay("Seed:", team.Seed.Value),
+ new RowDisplay("Last year's placing:", team.LastYearPlacing.Value > 0 ? $"#{team.LastYearPlacing:#,0}" : "0"),
+ new Container { Margin = new MarginPadding { Bottom = 30 } },
+ }
+ },
+ };
+
+ foreach (var p in team.Players)
+ fill.Add(new RowDisplay(p.Username, p.Statistics?.Ranks.Global?.ToString("\\##,0") ?? "-"));
+ }
+
+ internal class RowDisplay : CompositeDrawable
+ {
+ public RowDisplay(string left, string right)
+ {
+ AutoSizeAxes = Axes.Y;
+ RelativeSizeAxes = Axes.X;
+
+ var colour = OsuColour.Gray(0.3f);
+
+ InternalChildren = new Drawable[]
+ {
+ new TournamentSpriteText
+ {
+ Text = left,
+ Colour = colour,
+ Font = OsuFont.Torus.With(size: 22),
+ },
+ new TournamentSpriteText
+ {
+ Text = right,
+ Colour = colour,
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopLeft,
+ Font = OsuFont.Torus.With(size: 22, weight: FontWeight.Regular),
+ },
+ };
+ }
+ }
+
+ private class TeamDisplay : DrawableTournamentTeam
+ {
+ public TeamDisplay(TournamentTeam team)
+ : base(team)
+ {
+ AutoSizeAxes = Axes.Both;
+
+ Flag.RelativeSizeAxes = Axes.None;
+ Flag.Size = new Vector2(300, 200);
+ Flag.Scale = new Vector2(0.3f);
+
+ InternalChild = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 5),
+ Children = new Drawable[]
+ {
+ Flag,
+ new OsuSpriteText
+ {
+ Text = team?.FullName.Value ?? "???",
+ Font = OsuFont.Torus.With(size: 32, weight: FontWeight.SemiBold),
+ Colour = Color4.Black,
+ },
+ }
+ };
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs
index 47c923ff30..6559113f55 100644
--- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs
+++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs
@@ -7,12 +7,9 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
-using osu.Game.Tournament.Screens.Showcase;
using osuTK;
-using osuTK.Graphics;
namespace osu.Game.Tournament.Screens.TeamIntro
{
@@ -29,12 +26,11 @@ namespace osu.Game.Tournament.Screens.TeamIntro
InternalChildren = new Drawable[]
{
- new TourneyVideo(storage.GetStream(@"BG Team - Both OWC.m4v"))
+ new TourneyVideo(storage.GetStream(@"videos/teamintro.m4v"))
{
RelativeSizeAxes = Axes.Both,
Loop = true,
},
- new TournamentLogo(false),
mainContainer = new Container
{
RelativeSizeAxes = Axes.Both,
@@ -75,8 +71,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{
RelativeSizeAxes = Axes.Both,
Height = 0.25f,
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Y = 180,
}
};
}
@@ -85,8 +82,6 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{
public RoundDisplay(TournamentMatch match)
{
- var col = OsuColour.Gray(0.33f);
-
InternalChildren = new Drawable[]
{
new FillFlowContainer
@@ -98,31 +93,13 @@ namespace osu.Game.Tournament.Screens.TeamIntro
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
- new OsuSpriteText
+ new TournamentSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- Colour = col,
- Text = "COMING UP NEXT",
- Spacing = new Vector2(2, 0),
- Font = OsuFont.GetFont(size: 15, weight: FontWeight.Black)
- },
- new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Colour = col,
+ Colour = OsuColour.Gray(0.33f),
Text = match.Round.Value?.Name.Value ?? "Unknown Round",
- Spacing = new Vector2(10, 0),
- Font = OsuFont.GetFont(size: 50, weight: FontWeight.Light)
- },
- new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Colour = col,
- Text = match.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"),
- Font = OsuFont.GetFont(size: 20)
+ Font = OsuFont.Torus.With(size: 26, weight: FontWeight.Light)
},
}
}
@@ -132,21 +109,19 @@ namespace osu.Game.Tournament.Screens.TeamIntro
private class TeamWithPlayers : CompositeDrawable
{
- private readonly Color4 red = new Color4(129, 68, 65, 255);
- private readonly Color4 blue = new Color4(41, 91, 97, 255);
-
public TeamWithPlayers(TournamentTeam team, bool left = false)
{
FillFlowContainer players;
- var colour = left ? red : blue;
+ var colour = left ? TournamentGame.COLOUR_RED : TournamentGame.COLOUR_BLUE;
InternalChildren = new Drawable[]
{
- new TeamDisplay(team, left ? "Team Red" : "Team Blue", colour)
+ new TeamDisplay(team)
{
Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
- Origin = Anchor.Centre,
+ Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.Both,
- X = (left ? -1 : 1) * 0.36f,
+ X = (left ? -1 : 1) * 0.3145f,
+ Y = -0.077f,
},
players = new FillFlowContainer
{
@@ -157,7 +132,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
Origin = left ? Anchor.CentreRight : Anchor.CentreLeft,
RelativePositionAxes = Axes.Both,
- X = (left ? -1 : 1) * 0.66f,
+ X = (left ? -1 : 1) * 0.58f,
},
};
@@ -165,10 +140,10 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{
foreach (var p in team.Players)
{
- players.Add(new OsuSpriteText
+ players.Add(new TournamentSpriteText
{
Text = p.Username,
- Font = OsuFont.GetFont(size: 24),
+ Font = OsuFont.Torus.With(size: 24),
Colour = colour,
Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
Origin = left ? Anchor.CentreRight : Anchor.CentreLeft,
@@ -179,7 +154,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
private class TeamDisplay : DrawableTournamentTeam
{
- public TeamDisplay(TournamentTeam team, string teamName, Color4 colour)
+ public TeamDisplay(TournamentTeam team)
: base(team)
{
AutoSizeAxes = Axes.Both;
@@ -187,33 +162,24 @@ namespace osu.Game.Tournament.Screens.TeamIntro
Flag.Anchor = Flag.Origin = Anchor.TopCentre;
Flag.RelativeSizeAxes = Axes.None;
Flag.Size = new Vector2(300, 200);
- Flag.Scale = new Vector2(0.4f);
- Flag.Margin = new MarginPadding { Bottom = 20 };
+ Flag.Scale = new Vector2(0.32f);
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
- Spacing = new Vector2(0, 5),
+ Spacing = new Vector2(160),
Children = new Drawable[]
{
Flag,
- new OsuSpriteText
+ new TournamentSpriteText
{
- Text = team?.FullName.Value.ToUpper() ?? "???",
- Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 40, FontWeight.Light),
- Colour = Color4.Black,
+ Text = team?.FullName.Value ?? "???",
+ Font = OsuFont.Torus.With(size: 20, weight: FontWeight.Regular),
+ Colour = OsuColour.Gray(0.2f),
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
},
- new OsuSpriteText
- {
- Text = teamName.ToUpper(),
- Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 20, FontWeight.Regular),
- Colour = colour,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- }
}
};
}
diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs
index a0216c5db3..30b86f8421 100644
--- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs
+++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs
@@ -7,10 +7,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
-using osu.Game.Tournament.Screens.Showcase;
using osuTK;
using osuTK.Graphics;
@@ -33,22 +31,18 @@ namespace osu.Game.Tournament.Screens.TeamWin
InternalChildren = new Drawable[]
{
- blueWinVideo = new TourneyVideo(storage.GetStream(@"BG Team - Win Blue.m4v"))
+ blueWinVideo = new TourneyVideo(storage.GetStream(@"videos/teamwin-blue.m4v"))
{
Alpha = 1,
RelativeSizeAxes = Axes.Both,
Loop = true,
},
- redWinVideo = new TourneyVideo(storage.GetStream(@"BG Team - Win Red.m4v"))
+ redWinVideo = new TourneyVideo(storage.GetStream(@"videos/teamwin-red.m4v"))
{
Alpha = 0,
RelativeSizeAxes = Axes.Both,
Loop = true,
},
- new TournamentLogo(false)
- {
- Y = 40,
- },
mainContainer = new Container
{
RelativeSizeAxes = Axes.Both,
@@ -85,141 +79,99 @@ namespace osu.Game.Tournament.Screens.TeamWin
mainContainer.Children = new Drawable[]
{
+ new TeamFlagDisplay(match.Winner)
+ {
+ Size = new Vector2(300, 200),
+ Scale = new Vector2(0.5f),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ X = -387,
+ },
+ new TournamentSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.TopLeft,
+ Position = new Vector2(78, -70),
+ Colour = OsuColour.Gray(0.33f),
+ Text = match.Round.Value?.Name.Value ?? "Unknown Round",
+ Font = OsuFont.Torus.With(size: 30, weight: FontWeight.Regular)
+ },
new TeamWithPlayers(match.Winner, redWin)
{
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
Height = 0.6f,
Anchor = Anchor.Centre,
- Origin = Anchor.Centre
+ Origin = Anchor.TopLeft,
+ Position = new Vector2(78, 0),
},
- new RoundDisplay(match)
- {
- RelativeSizeAxes = Axes.Both,
- Height = 0.25f,
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- }
};
}
- private class RoundDisplay : CompositeDrawable
- {
- public RoundDisplay(TournamentMatch match)
- {
- var col = OsuColour.Gray(0.33f);
-
- InternalChildren = new Drawable[]
- {
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Direction = FillDirection.Vertical,
- Spacing = new Vector2(0, 10),
- Children = new Drawable[]
- {
- new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Colour = col,
- Text = "WINNER",
- Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 15, FontWeight.Regular),
- },
- new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Colour = col,
- Text = match.Round.Value?.Name.Value ?? "Unknown Round",
- Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 50, FontWeight.Light),
- Spacing = new Vector2(10, 0),
- },
- new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Colour = col,
- Text = match.Date.Value.ToUniversalTime().ToString("dd MMMM HH:mm UTC"),
- Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 20, FontWeight.Light),
- },
- }
- }
- };
- }
- }
-
private class TeamWithPlayers : CompositeDrawable
{
- private readonly Color4 red = new Color4(129, 68, 65, 255);
- private readonly Color4 blue = new Color4(41, 91, 97, 255);
-
public TeamWithPlayers(TournamentTeam team, bool left = false)
{
- var colour = left ? red : blue;
+ FillFlowContainer players;
+
+ var colour = left ? TournamentGame.COLOUR_RED : TournamentGame.COLOUR_BLUE;
InternalChildren = new Drawable[]
{
- new TeamDisplay(team, left ? "Team Red" : "Team Blue", colour)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- },
new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
- Spacing = new Vector2(0, 5),
- Padding = new MarginPadding(20),
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativePositionAxes = Axes.Both,
- },
- };
- }
-
- private class TeamDisplay : DrawableTournamentTeam
- {
- public TeamDisplay(TournamentTeam team, string teamName, Color4 colour)
- : base(team)
- {
- AutoSizeAxes = Axes.Both;
-
- Flag.Anchor = Flag.Origin = Anchor.TopCentre;
- Flag.RelativeSizeAxes = Axes.None;
- Flag.Size = new Vector2(300, 200);
- Flag.Scale = new Vector2(0.4f);
- Flag.Margin = new MarginPadding { Bottom = 20 };
-
- InternalChild = new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Vertical,
- Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
- Flag,
- new OsuSpriteText
+ new TournamentSpriteText
{
- Text = team?.FullName.Value.ToUpper() ?? "???",
- Font = TournamentFont.GetFont(TournamentTypeface.Aquatico, 40, FontWeight.Light),
+ Text = "WINNER",
+ Font = OsuFont.Torus.With(size: 24, weight: FontWeight.SemiBold),
Colour = Color4.Black,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
},
- new OsuSpriteText
+ new TournamentSpriteText
{
- Text = teamName.ToUpper(),
- Font = OsuFont.GetFont(size: 20),
- Colour = colour,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- }
+ Text = team?.FullName.Value ?? "???",
+ Font = OsuFont.Torus.With(size: 30, weight: FontWeight.SemiBold),
+ Colour = Color4.Black,
+ },
+ players = new FillFlowContainer
+ {
+ Direction = FillDirection.Vertical,
+ AutoSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Top = 10 },
+ },
}
- };
+ },
+ };
+
+ if (team != null)
+ {
+ foreach (var p in team.Players)
+ {
+ players.Add(new TournamentSpriteText
+ {
+ Text = p.Username,
+ Font = OsuFont.Torus.With(size: 24),
+ Colour = colour,
+ Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft,
+ Origin = left ? Anchor.CentreRight : Anchor.CentreLeft,
+ });
+ }
}
}
}
+
+ private class TeamFlagDisplay : DrawableTournamentTeam
+ {
+ public TeamFlagDisplay(TournamentTeam team)
+ : base(team)
+ {
+ InternalChildren = new Drawable[]
+ {
+ Flag
+ };
+ }
+ }
}
}
diff --git a/osu.Game.Tournament/TournamentFont.cs b/osu.Game.Tournament/TournamentFont.cs
deleted file mode 100644
index 32f0264562..0000000000
--- a/osu.Game.Tournament/TournamentFont.cs
+++ /dev/null
@@ -1,75 +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 osu.Framework.Graphics.Sprites;
-using osu.Game.Graphics;
-
-namespace osu.Game.Tournament
-{
- public static class TournamentFont
- {
- ///
- /// The default font size.
- ///
- public const float DEFAULT_FONT_SIZE = 16;
-
- ///
- /// Retrieves a .
- ///
- /// The font typeface.
- /// The size of the text in local space. For a value of 16, a single line will have a height of 16px.
- /// The font weight.
- /// Whether the font is italic.
- /// Whether all characters should be spaced the same distance apart.
- /// The .
- public static FontUsage GetFont(TournamentTypeface typeface = TournamentTypeface.Aquatico, float size = DEFAULT_FONT_SIZE, FontWeight weight = FontWeight.Medium, bool italics = false, bool fixedWidth = false)
- => new FontUsage(GetFamilyString(typeface), size, GetWeightString(typeface, weight), italics, fixedWidth);
-
- ///
- /// Retrieves the string representation of a .
- ///
- /// The .
- /// The string representation.
- public static string GetFamilyString(TournamentTypeface typeface)
- {
- switch (typeface)
- {
- case TournamentTypeface.Aquatico:
- return "Aquatico";
- }
-
- return null;
- }
-
- ///
- /// Retrieves the string representation of a .
- ///
- /// The .
- /// The .
- /// The string representation of in the specified .
- public static string GetWeightString(TournamentTypeface typeface, FontWeight weight)
- => GetWeightString(GetFamilyString(typeface), weight);
-
- ///
- /// Retrieves the string representation of a .
- ///
- /// The family string.
- /// The .
- /// The string representation of in the specified .
- public static string GetWeightString(string family, FontWeight weight)
- {
- string weightString = weight.ToString();
-
- // Only exo has an explicit "regular" weight, other fonts do not
- if (weight == FontWeight.Regular && family != GetFamilyString(TournamentTypeface.Aquatico))
- weightString = string.Empty;
-
- return weightString;
- }
- }
-
- public enum TournamentTypeface
- {
- Aquatico
- }
-}
diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs
index 7dbcf37af6..608fc5f04a 100644
--- a/osu.Game.Tournament/TournamentGame.cs
+++ b/osu.Game.Tournament/TournamentGame.cs
@@ -3,11 +3,15 @@
using osu.Framework.Graphics;
using osu.Game.Graphics.Cursor;
+using osuTK.Graphics;
namespace osu.Game.Tournament
{
public class TournamentGame : TournamentGameBase
{
+ public static readonly Color4 COLOUR_RED = new Color4(144, 0, 0, 255);
+ public static readonly Color4 COLOUR_BLUE = new Color4(0, 84, 144, 255);
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs
index 1c94856a4e..435f315c8d 100644
--- a/osu.Game.Tournament/TournamentGameBase.cs
+++ b/osu.Game.Tournament/TournamentGameBase.cs
@@ -18,7 +18,6 @@ using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models;
@@ -54,9 +53,6 @@ namespace osu.Game.Tournament
{
Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly));
- AddFont(Resources, @"Resources/Fonts/Aquatico-Regular");
- AddFont(Resources, @"Resources/Fonts/Aquatico-Light");
-
Textures.AddStore(new TextureLoaderStore(new ResourceStore(new StorageBackedResourceStore(storage))));
this.storage = storage;
@@ -104,10 +100,10 @@ namespace osu.Game.Tournament
Colour = Color4.Red,
RelativeSizeAxes = Axes.Both,
},
- new OsuSpriteText
+ new TournamentSpriteText
{
Text = "Please make the window wider",
- Font = OsuFont.Default.With(weight: "bold"),
+ Font = OsuFont.Torus.With(weight: FontWeight.Bold),
Colour = Color4.White,
Padding = new MarginPadding(20)
}
@@ -124,10 +120,9 @@ namespace osu.Game.Tournament
using (var sr = new StreamReader(stream))
ladder = JsonConvert.DeserializeObject(sr.ReadToEnd());
}
- else
- {
+
+ if (ladder == null)
ladder = new LadderInfo();
- }
if (ladder.Ruleset.Value == null)
ladder.Ruleset.Value = RulesetStore.AvailableRulesets.First();
@@ -205,9 +200,11 @@ namespace osu.Game.Tournament
{
foreach (var p in t.Players)
{
- if (p.Username == null || p.Statistics == null)
+ if (string.IsNullOrEmpty(p.Username) || p.Statistics == null)
+ {
PopulateUser(p);
- addedInfo = true;
+ addedInfo = true;
+ }
}
}
@@ -243,6 +240,24 @@ namespace osu.Game.Tournament
}
}
+ foreach (var t in ladder.Teams)
+ {
+ foreach (var s in t.SeedingResults)
+ {
+ foreach (var b in s.Beatmaps)
+ {
+ if (b.BeatmapInfo == null && b.ID > 0)
+ {
+ var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
+ req.Perform(API);
+ b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore);
+
+ addedInfo = true;
+ }
+ }
+ }
+ }
+
return addedInfo;
}
diff --git a/osu.Game.Tournament/TournamentSceneManager.cs b/osu.Game.Tournament/TournamentSceneManager.cs
index de3d685c31..9f5f2b6827 100644
--- a/osu.Game.Tournament/TournamentSceneManager.cs
+++ b/osu.Game.Tournament/TournamentSceneManager.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Tournament
//Masking = true,
Children = new Drawable[]
{
- video = new TourneyVideo(storage.GetStream("BG Logoless - OWC.m4v"))
+ video = new TourneyVideo(storage.GetStream("videos/main.m4v"))
{
Loop = true,
RelativeSizeAxes = Axes.Both,
@@ -80,6 +80,7 @@ namespace osu.Game.Tournament
new ShowcaseScreen(),
new MapPoolScreen(),
new TeamIntroScreen(),
+ new SeedingScreen(),
new DrawingsScreen(),
new GameplayScreen(),
new TeamWinScreen()
@@ -121,6 +122,7 @@ namespace osu.Game.Tournament
new ScreenButton(typeof(LadderScreen)) { Text = "Bracket", RequestSelection = SetScreen },
new Separator(),
new ScreenButton(typeof(TeamIntroScreen)) { Text = "TeamIntro", RequestSelection = SetScreen },
+ new ScreenButton(typeof(SeedingScreen)) { Text = "Seeding", RequestSelection = SetScreen },
new Separator(),
new ScreenButton(typeof(MapPoolScreen)) { Text = "MapPool", RequestSelection = SetScreen },
new ScreenButton(typeof(GameplayScreen)) { Text = "Gameplay", RequestSelection = SetScreen },
@@ -146,8 +148,20 @@ namespace osu.Game.Tournament
private Drawable currentScreen;
private ScheduledDelegate scheduledHide;
+ private Drawable temporaryScreen;
+
+ public void SetScreen(Drawable screen)
+ {
+ currentScreen?.Hide();
+ currentScreen = null;
+
+ screens.Add(temporaryScreen = screen);
+ }
+
public void SetScreen(Type screenType)
{
+ temporaryScreen?.Expire();
+
var target = screens.FirstOrDefault(s => s.GetType() == screenType);
if (target == null || currentScreen == target) return;
diff --git a/osu.Game.Tournament/TournamentSpriteText.cs b/osu.Game.Tournament/TournamentSpriteText.cs
new file mode 100644
index 0000000000..e550dfbfae
--- /dev/null
+++ b/osu.Game.Tournament/TournamentSpriteText.cs
@@ -0,0 +1,16 @@
+// 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.Graphics;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Tournament
+{
+ public class TournamentSpriteText : OsuSpriteText
+ {
+ public TournamentSpriteText()
+ {
+ Font = OsuFont.Torus;
+ }
+ }
+}
diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs
index 6f0b62543d..862be41c1a 100644
--- a/osu.Game/Audio/PreviewTrackManager.cs
+++ b/osu.Game/Audio/PreviewTrackManager.cs
@@ -19,13 +19,15 @@ namespace osu.Game.Audio
{
private readonly BindableDouble muteBindable = new BindableDouble();
- private AudioManager audio;
+ [Resolved]
+ private AudioManager audio { get; set; }
+
private PreviewTrackStore trackStore;
protected TrackManagerPreviewTrack CurrentTrack;
[BackgroundDependencyLoader]
- private void load(AudioManager audio)
+ private void load()
{
// this is a temporary solution to get around muting ourselves.
// todo: update this once we have a BackgroundTrackManager or similar.
@@ -33,8 +35,6 @@ namespace osu.Game.Audio
audio.AddItem(trackStore);
trackStore.AddAdjustment(AdjustableProperty.Volume, audio.VolumeTrack);
-
- this.audio = audio;
}
///
@@ -90,6 +90,7 @@ namespace osu.Game.Audio
public class TrackManagerPreviewTrack : PreviewTrack
{
+ [Resolved]
public IPreviewTrackOwner Owner { get; private set; }
private readonly BeatmapSetInfo beatmapSetInfo;
@@ -101,12 +102,6 @@ namespace osu.Game.Audio
this.trackManager = trackManager;
}
- [BackgroundDependencyLoader]
- private void load(IPreviewTrackOwner owner)
- {
- Owner = owner;
- }
-
protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3");
}
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index bcc9ab885e..68d113ce40 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -51,6 +51,9 @@ namespace osu.Game.Beatmaps
[NotMapped]
public BeatmapOnlineInfo OnlineInfo { get; set; }
+ [NotMapped]
+ public int? MaxCombo { get; set; }
+
///
/// The playable length in milliseconds of this beatmap.
///
diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
index f9d71a2a6e..1991770518 100644
--- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
@@ -36,8 +36,9 @@ namespace osu.Game.Beatmaps
using (var stream = new LineBufferedReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
return Decoder.GetDecoder(stream).Decode(stream);
}
- catch
+ catch (Exception e)
{
+ Logger.Error(e, "Beatmap failed to load");
return null;
}
}
@@ -59,8 +60,9 @@ namespace osu.Game.Beatmaps
{
return textureStore.Get(getPathForFile(Metadata.BackgroundFile));
}
- catch
+ catch (Exception e)
{
+ Logger.Error(e, "Background failed to load");
return null;
}
}
@@ -72,10 +74,13 @@ namespace osu.Game.Beatmaps
try
{
- return new VideoSprite(textureStore.GetStream(getPathForFile(Metadata.VideoFile)));
+ var stream = textureStore.GetStream(getPathForFile(Metadata.VideoFile));
+
+ return stream == null ? null : new VideoSprite(stream);
}
- catch
+ catch (Exception e)
{
+ Logger.Error(e, "Video failed to load");
return null;
}
}
@@ -86,8 +91,9 @@ namespace osu.Game.Beatmaps
{
return (trackStore ??= AudioManager.GetTrackStore(store)).Get(getPathForFile(Metadata.AudioFile));
}
- catch
+ catch (Exception e)
{
+ Logger.Error(e, "Track failed to load");
return null;
}
}
@@ -115,8 +121,9 @@ namespace osu.Game.Beatmaps
var trackData = store.GetStream(getPathForFile(Metadata.AudioFile));
return trackData == null ? null : new Waveform(trackData);
}
- catch
+ catch (Exception e)
{
+ Logger.Error(e, "Waveform failed to load");
return null;
}
}
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
index 351e5df17a..f6e03d40ff 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
@@ -13,6 +13,7 @@ namespace osu.Game.Beatmaps.Drawables
public class BeatmapSetOnlineStatusPill : CircularContainer
{
private readonly OsuSpriteText statusText;
+ private readonly Box background;
private BeatmapSetOnlineStatus status;
@@ -43,6 +44,12 @@ namespace osu.Game.Beatmaps.Drawables
set => statusText.Padding = value;
}
+ public Color4 BackgroundColour
+ {
+ get => background.Colour;
+ set => background.Colour = value;
+ }
+
public BeatmapSetOnlineStatusPill()
{
AutoSizeAxes = Axes.Both;
@@ -50,7 +57,7 @@ namespace osu.Game.Beatmaps.Drawables
Children = new Drawable[]
{
- new Box
+ background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
index a3128e36c4..8a0d981e49 100644
--- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
+++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
@@ -150,12 +150,12 @@ namespace osu.Game.Beatmaps.Drawables
};
}
- private OsuColour colours;
+ [Resolved]
+ private OsuColour colours { get; set; }
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load()
{
- this.colours = colours;
background.Colour = colours.Gray3;
}
diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
index 30346a8a96..eb05cbaf85 100644
--- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
@@ -16,6 +16,8 @@ namespace osu.Game.Beatmaps.Drawables
{
public readonly Bindable Beatmap = new Bindable();
+ protected override double LoadDelay => 500;
+
[Resolved]
private BeatmapManager beatmaps { get; set; }
diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs
index 16eecb7198..c60bd0286e 100644
--- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs
+++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
@@ -50,7 +49,7 @@ namespace osu.Game.Beatmaps.Drawables
Child = new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.1f)),
+ Colour = OsuColour.Gray(0.2f),
};
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 009da0656b..4b01b2490e 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps.Formats
hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty);
}
- protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal);
+ protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(' ') || line.StartsWith('_');
protected override void ParseLine(Beatmap beatmap, Section section, string line)
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 0ec80eee41..e28e235788 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps.Formats
if (ShouldSkipLine(line))
continue;
- if (line.StartsWith(@"[", StringComparison.Ordinal) && line.EndsWith(@"]", StringComparison.Ordinal))
+ if (line.StartsWith('[') && line.EndsWith(']'))
{
if (!Enum.TryParse(line[1..^1], out section))
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index 35576e0f33..6569f76b2d 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -64,15 +64,16 @@ namespace osu.Game.Beatmaps.Formats
private void handleEvents(string line)
{
var depth = 0;
- var lineSpan = line.AsSpan();
- while (lineSpan.StartsWith(" ", StringComparison.Ordinal) || lineSpan.StartsWith("_", StringComparison.Ordinal))
+ foreach (char c in line)
{
- lineSpan = lineSpan.Slice(1);
- ++depth;
+ if (c == ' ' || c == '_')
+ depth++;
+ else
+ break;
}
- line = lineSpan.ToString();
+ line = line.Substring(depth);
decodeVariables(ref line);
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 05c344b199..1e1ffad81e 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -17,10 +17,11 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
using osu.Framework.Graphics.Video;
+using osu.Framework.Logging;
namespace osu.Game.Beatmaps
{
- public abstract class WorkingBeatmap : IWorkingBeatmap, IDisposable
+ public abstract class WorkingBeatmap : IWorkingBeatmap
{
public readonly BeatmapInfo BeatmapInfo;
@@ -39,7 +40,7 @@ namespace osu.Game.Beatmaps
BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
- track = new RecyclableLazy
public virtual void RecycleTrack() => track.Recycle();
- #region Disposal
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private bool isDisposed;
-
- protected virtual void Dispose(bool isDisposing)
- {
- if (isDisposed)
- return;
-
- isDisposed = true;
-
- // recycling logic is not here for the time being, as components which use
- // retrieved objects from WorkingBeatmap may not hold a reference to the WorkingBeatmap itself.
- // this should be fine as each retrieved component do have their own finalizers.
-
- // cancelling the beatmap load is safe for now since the retrieval is a synchronous
- // operation. if we add an async retrieval method this may need to be reconsidered.
- beatmapCancellation?.Cancel();
- total_count.Value--;
- }
-
~WorkingBeatmap()
{
- Dispose(false);
+ total_count.Value--;
}
- #endregion
-
public class RecyclableLazy
{
private Lazy lazy;
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 4155381790..ce959e9057 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
Set(OsuSetting.Skin, 0, -1, int.MaxValue);
- Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details);
+ Set(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details);
Set(OsuSetting.ShowConvertedBeatmaps, true);
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
@@ -78,7 +78,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.MenuParallax, true);
// Gameplay
- Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
+ Set(OsuSetting.DimLevel, 0.8, 0, 1, 0.01);
Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
Set(OsuSetting.LightenDuringBreaks, true);
diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs
index a3788e4582..fe487cb1d0 100644
--- a/osu.Game/Configuration/SettingSourceAttribute.cs
+++ b/osu.Game/Configuration/SettingSourceAttribute.cs
@@ -3,11 +3,11 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
namespace osu.Game.Configuration
@@ -16,6 +16,10 @@ namespace osu.Game.Configuration
/// An attribute to mark a bindable as being exposed to the user via settings controls.
/// Can be used in conjunction with to automatically create UI controls.
///
+ ///
+ /// All controls with set will be placed first in ascending order.
+ /// All controls with no will come afterward in default order.
+ ///
[MeansImplicitUse]
[AttributeUsage(AttributeTargets.Property)]
public class SettingSourceAttribute : Attribute
@@ -24,18 +28,26 @@ namespace osu.Game.Configuration
public string Description { get; }
+ public int? OrderPosition { get; }
+
public SettingSourceAttribute(string label, string description = null)
{
Label = label ?? string.Empty;
Description = description ?? string.Empty;
}
+
+ public SettingSourceAttribute(string label, string description, int orderPosition)
+ : this(label, description)
+ {
+ OrderPosition = orderPosition;
+ }
}
public static class SettingSourceExtensions
{
public static IEnumerable CreateSettingsControls(this object obj)
{
- foreach (var (attr, property) in obj.GetSettingsSourceProperties())
+ foreach (var (attr, property) in obj.GetOrderedSettingsSourceProperties())
{
object value = property.GetValue(obj);
@@ -92,7 +104,8 @@ namespace osu.Game.Configuration
var dropdownType = typeof(SettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]);
var dropdown = (Drawable)Activator.CreateInstance(dropdownType);
- dropdown.GetType().GetProperty(nameof(IHasCurrentValue