diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
similarity index 100%
rename from ISSUE_TEMPLATE.md
rename to .github/ISSUE_TEMPLATE.md
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
deleted file mode 100644
index 221e4746cb..0000000000
--- a/.github/pull_request_template.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Add any details pertaining to developers above the break.
-
-- [ ] Depends on #PR
-- Closes #ISSUE
-
----
-
-Add a sentence or two describing this change in plain english. This will be displayed on the [changelog](https://osu.ppy.sh/home/changelog). A single screenshot or short gif is also welcomed.
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index f95a04e517..0e2850a01c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,8 +11,10 @@
*.userprefs
### Cake ###
-tools/*
-!tools/cakebuild.csproj
+tools/**
+build/tools/**
+
+fastlane/report.xml
# Build results
bin/[Dd]ebug/
diff --git a/.gitmodules b/.gitmodules
index f1c4f5d172..e69de29bb2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "osu-resources"]
- path = osu-resources
- url = https://github.com/ppy/osu-resources
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 10c6f02314..c3306c2db7 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -68,6 +68,20 @@
}
},
"console": "internalConsole"
+ },
+ {
+ "name": "Cake: Debug Script",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "${workspaceRoot}/build/tools/Cake.CoreCLR/0.30.0/Cake.dll",
+ "args": [
+ "${workspaceRoot}/build/build.cake",
+ "--debug",
+ "--verbosity=diagnostic"
+ ],
+ "cwd": "${workspaceRoot}/build",
+ "stopAtEntry": true,
+ "externalConsole": false
}
]
}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 2e9bec22c2..de799a7c03 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -70,7 +70,8 @@
"type": "shell",
"command": "dotnet",
"args": [
- "restore"
+ "restore",
+ "osu.sln"
],
"problemMatcher": []
}
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000000..cdd3a6b349
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,6 @@
+source "https://rubygems.org"
+
+gem "fastlane"
+
+plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
+eval_gemfile(plugins_path) if File.exist?(plugins_path)
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000000..17c0db12e7
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,173 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ CFPropertyList (3.0.0)
+ addressable (2.6.0)
+ public_suffix (>= 2.0.2, < 4.0)
+ atomos (0.1.3)
+ babosa (1.0.2)
+ claide (1.0.2)
+ colored (1.2)
+ colored2 (3.1.2)
+ commander-fastlane (4.4.6)
+ highline (~> 1.7.2)
+ declarative (0.0.10)
+ declarative-option (0.1.0)
+ digest-crc (0.4.1)
+ domain_name (0.5.20180417)
+ unf (>= 0.0.5, < 1.0.0)
+ dotenv (2.7.1)
+ emoji_regex (1.0.1)
+ excon (0.62.0)
+ faraday (0.15.4)
+ multipart-post (>= 1.2, < 3)
+ faraday-cookie_jar (0.0.6)
+ faraday (>= 0.7.4)
+ http-cookie (~> 1.0.0)
+ faraday_middleware (0.13.1)
+ faraday (>= 0.7.4, < 1.0)
+ fastimage (2.1.5)
+ fastlane (2.117.0)
+ CFPropertyList (>= 2.3, < 4.0.0)
+ addressable (>= 2.3, < 3.0.0)
+ babosa (>= 1.0.2, < 2.0.0)
+ bundler (>= 1.12.0, < 3.0.0)
+ colored
+ commander-fastlane (>= 4.4.6, < 5.0.0)
+ dotenv (>= 2.1.1, < 3.0.0)
+ emoji_regex (>= 0.1, < 2.0)
+ excon (>= 0.45.0, < 1.0.0)
+ faraday (~> 0.9)
+ faraday-cookie_jar (~> 0.0.6)
+ faraday_middleware (~> 0.9)
+ fastimage (>= 2.1.0, < 3.0.0)
+ gh_inspector (>= 1.1.2, < 2.0.0)
+ google-api-client (>= 0.21.2, < 0.24.0)
+ google-cloud-storage (>= 1.15.0, < 2.0.0)
+ highline (>= 1.7.2, < 2.0.0)
+ json (< 3.0.0)
+ mini_magick (~> 4.5.1)
+ multi_json
+ multi_xml (~> 0.5)
+ multipart-post (~> 2.0.0)
+ plist (>= 3.1.0, < 4.0.0)
+ public_suffix (~> 2.0.0)
+ rubyzip (>= 1.2.2, < 2.0.0)
+ security (= 0.1.3)
+ simctl (~> 1.6.3)
+ slack-notifier (>= 2.0.0, < 3.0.0)
+ terminal-notifier (>= 1.6.2, < 2.0.0)
+ terminal-table (>= 1.4.5, < 2.0.0)
+ tty-screen (>= 0.6.3, < 1.0.0)
+ tty-spinner (>= 0.8.0, < 1.0.0)
+ word_wrap (~> 1.0.0)
+ xcodeproj (>= 1.6.0, < 2.0.0)
+ xcpretty (~> 0.3.0)
+ xcpretty-travis-formatter (>= 0.0.3)
+ fastlane-plugin-clean_testflight_testers (0.2.0)
+ fastlane-plugin-souyuz (0.8.1)
+ souyuz (>= 0.8.1)
+ fastlane-plugin-xamarin (0.6.3)
+ gh_inspector (1.1.3)
+ google-api-client (0.23.9)
+ addressable (~> 2.5, >= 2.5.1)
+ googleauth (>= 0.5, < 0.7.0)
+ httpclient (>= 2.8.1, < 3.0)
+ mime-types (~> 3.0)
+ representable (~> 3.0)
+ retriable (>= 2.0, < 4.0)
+ signet (~> 0.9)
+ google-cloud-core (1.3.0)
+ google-cloud-env (~> 1.0)
+ google-cloud-env (1.0.5)
+ faraday (~> 0.11)
+ google-cloud-storage (1.16.0)
+ digest-crc (~> 0.4)
+ google-api-client (~> 0.23)
+ google-cloud-core (~> 1.2)
+ googleauth (>= 0.6.2, < 0.10.0)
+ googleauth (0.6.7)
+ faraday (~> 0.12)
+ jwt (>= 1.4, < 3.0)
+ memoist (~> 0.16)
+ multi_json (~> 1.11)
+ os (>= 0.9, < 2.0)
+ signet (~> 0.7)
+ highline (1.7.10)
+ http-cookie (1.0.3)
+ domain_name (~> 0.5)
+ httpclient (2.8.3)
+ json (2.2.0)
+ jwt (2.1.0)
+ memoist (0.16.0)
+ mime-types (3.2.2)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2018.0812)
+ mini_magick (4.5.1)
+ mini_portile2 (2.4.0)
+ multi_json (1.13.1)
+ multi_xml (0.6.0)
+ multipart-post (2.0.0)
+ nanaimo (0.2.6)
+ naturally (2.2.0)
+ nokogiri (1.10.1)
+ mini_portile2 (~> 2.4.0)
+ os (1.0.0)
+ plist (3.5.0)
+ public_suffix (2.0.5)
+ representable (3.0.4)
+ declarative (< 0.1.0)
+ declarative-option (< 0.2.0)
+ uber (< 0.2.0)
+ retriable (3.1.2)
+ rouge (2.0.7)
+ rubyzip (1.2.2)
+ security (0.1.3)
+ signet (0.11.0)
+ addressable (~> 2.3)
+ faraday (~> 0.9)
+ jwt (>= 1.5, < 3.0)
+ multi_json (~> 1.10)
+ simctl (1.6.5)
+ CFPropertyList
+ naturally
+ slack-notifier (2.3.2)
+ souyuz (0.8.1)
+ fastlane (>= 2.29.0)
+ highline (~> 1.7)
+ nokogiri (~> 1.7)
+ terminal-notifier (1.8.0)
+ terminal-table (1.8.0)
+ unicode-display_width (~> 1.1, >= 1.1.1)
+ tty-cursor (0.6.1)
+ tty-screen (0.6.5)
+ tty-spinner (0.9.0)
+ tty-cursor (~> 0.6.0)
+ uber (0.1.0)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.7.5)
+ unicode-display_width (1.4.1)
+ word_wrap (1.0.0)
+ xcodeproj (1.8.1)
+ CFPropertyList (>= 2.3.3, < 4.0)
+ atomos (~> 0.1.3)
+ claide (>= 1.0.2, < 2.0)
+ colored2 (~> 3.1)
+ nanaimo (~> 0.2.6)
+ xcpretty (0.3.0)
+ rouge (~> 2.0.7)
+ xcpretty-travis-formatter (1.0.0)
+ xcpretty (~> 0.2, >= 0.0.7)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ fastlane
+ fastlane-plugin-clean_testflight_testers
+ fastlane-plugin-souyuz
+ fastlane-plugin-xamarin
+
+BUNDLED WITH
+ 2.0.1
diff --git a/README.md b/README.md
index a54b28b74a..abddb1faa1 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,24 @@
-# osu! [![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy)
+# osu!
+
+[![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy)
Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commonly known by the codename "osu!lazer". Pew pew.
-# Status
+## 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.
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.
-# Requirements
+## Requirements
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
- Note that there are **[additional requirements for Windows 7 and Windows 8.1](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** which you may need to manually install if your operating system is not up-to-date.
-# Running osu!
+## Running osu!
-## Releases
+### Releases
If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled.
@@ -26,26 +28,22 @@ If you are not interested in developing the game, please head over to the [relea
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
-## Downloading the source code
+### Downloading the source code
Clone the repository **including submodules**:
```shell
-git clone --recurse-submodules https://github.com/ppy/osu
+git clone https://github.com/ppy/osu
cd osu
```
-> If you forgot the `--recurse-submodules` option, run this command inside the `osu` directory:
->
-> `git submodule update --init --recursive`
-
To update the source code to the latest commit, run the following command inside the `osu` directory:
```shell
-git pull --recurse-submodules
+git pull
```
-## Building
+### Building
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided below.
@@ -61,7 +59,7 @@ If you are not interested in debugging osu!, you can add `-c Release` to gain pe
If the build fails, try to restore nuget packages with `dotnet restore`.
-### A note for Linux users
+#### A note for Linux users
On Linux, the environment variable `LD_LIBRARY_PATH` must point to the build directory, located at `osu.Desktop/bin/Debug/$NETCORE_VERSION`.
@@ -73,11 +71,15 @@ For example, you can run osu! with the following command:
LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp2.2" dotnet run --project osu.Desktop
```
-## Code analysis
+### Testing with resource/framework modifications
-Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternative, you can install resharper or use rider to get inline support in your IDE of choice.
+Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be achieved by running some commands as documented on the [osu-resources](https://github.com/ppy/osu-resources/wiki/Testing-local-resources-checkout-with-other-projects) and [osu-framework](https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects) wiki pages.
-# Contributing
+### Code analysis
+
+Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install resharper or use rider to get inline support in your IDE of choice.
+
+## Contributing
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted.
@@ -87,7 +89,7 @@ Contributions can be made via pull requests to this repository. We hope to credi
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.
-# Licence
+## Licence
The osu! client code and framework are licensed under the [MIT licence](https://opensource.org/licenses/MIT). Please see [the licence file](LICENCE) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source.
diff --git a/build.ps1 b/build.ps1
index 9968673c90..c6a0bf6d4a 100644
--- a/build.ps1
+++ b/build.ps1
@@ -41,27 +41,28 @@ Param(
[switch]$ShowDescription,
[Alias("WhatIf", "Noop")]
[switch]$DryRun,
- [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
+ [Parameter(Position = 0, Mandatory = $false, ValueFromRemainingArguments = $true)]
[string[]]$ScriptArgs
)
Write-Host "Preparing to run build script..."
# Determine the script root for resolving other paths.
-if(!$PSScriptRoot){
+if(!$PSScriptRoot) {
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
}
# Resolve the paths for resources used for debugging.
-$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
-$CAKE_CSPROJ = Join-Path $TOOLS_DIR "cakebuild.csproj"
+$BUILD_DIR = Join-Path $PSScriptRoot "build"
+$TOOLS_DIR = Join-Path $BUILD_DIR "tools"
+$CAKE_CSPROJ = Join-Path $BUILD_DIR "cakebuild.csproj"
# Install the required tools locally.
Write-Host "Restoring cake tools..."
Invoke-Expression "dotnet restore `"$CAKE_CSPROJ`" --packages `"$TOOLS_DIR`"" | Out-Null
# Find the Cake executable
-$CAKE_EXECUTABLE = (Get-ChildItem -Path ./tools/cake.coreclr/ -Filter Cake.dll -Recurse).FullName
+$CAKE_EXECUTABLE = (Get-ChildItem -Path "$TOOLS_DIR/cake.coreclr/" -Filter Cake.dll -Recurse).FullName
# Build Cake arguments
$cakeArguments = @("$Script");
@@ -75,5 +76,7 @@ $cakeArguments += $ScriptArgs
# Start Cake
Write-Host "Running build script..."
+Push-Location -Path $BUILD_DIR
Invoke-Expression "dotnet `"$CAKE_EXECUTABLE`" $cakeArguments"
+Pop-Location
exit $LASTEXITCODE
diff --git a/build.sh b/build.sh
index caf1702f41..8f1ef5b455 100755
--- a/build.sh
+++ b/build.sh
@@ -6,12 +6,13 @@
echo "Preparing to run build script..."
+cd build
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
TOOLS_DIR=$SCRIPT_DIR/tools
CAKE_BINARY_PATH=$TOOLS_DIR/"cake.coreclr"
SCRIPT="build.cake"
-CAKE_CSPROJ=$TOOLS_DIR/"cakebuild.csproj"
+CAKE_CSPROJ=$SCRIPT_DIR/"cakebuild.csproj"
# Parse arguments.
CAKE_ARGUMENTS=()
diff --git a/build.cake b/build/build.cake
similarity index 75%
rename from build.cake
rename to build/build.cake
index bc7dfafb8c..81deeb3bc7 100644
--- a/build.cake
+++ b/build/build.cake
@@ -1,6 +1,7 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.21"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.2"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
+var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
///////////////////////////////////////////////////////////////////////////////
// ARGUMENTS
@@ -9,30 +10,24 @@
var target = Argument("target", "Build");
var configuration = Argument("configuration", "Release");
-var osuSolution = new FilePath("./osu.sln");
+var rootDirectory = new DirectoryPath("..");
+var solution = rootDirectory.CombineWithFilePath("osu.sln");
///////////////////////////////////////////////////////////////////////////////
// TASKS
///////////////////////////////////////////////////////////////////////////////
-Task("Restore")
- .Does(() => {
- DotNetCoreRestore(osuSolution.FullPath);
- });
-
Task("Compile")
- .IsDependentOn("Restore")
.Does(() => {
- DotNetCoreBuild(osuSolution.FullPath, new DotNetCoreBuildSettings {
+ DotNetCoreBuild(solution.FullPath, new DotNetCoreBuildSettings {
Configuration = configuration,
- NoRestore = true,
});
});
Task("Test")
.IsDependentOn("Compile")
.Does(() => {
- var testAssemblies = GetFiles("**/*.Tests/bin/**/*.Tests.dll");
+ var testAssemblies = GetFiles(rootDirectory + "/**/*.Tests/bin/**/*.Tests.dll");
DotNetCoreVSTest(testAssemblies, new DotNetCoreVSTestSettings {
Logger = AppVeyor.IsRunningOnAppVeyor ? "Appveyor" : $"trx",
@@ -46,9 +41,7 @@ Task("InspectCode")
.WithCriteria(IsRunningOnWindows())
.IsDependentOn("Compile")
.Does(() => {
- var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
-
- InspectCode(osuSolution, new InspectCodeSettings {
+ InspectCode(solution, new InspectCodeSettings {
CachesHome = "inspectcode",
OutputFile = "inspectcodereport.xml",
});
@@ -59,7 +52,7 @@ Task("InspectCode")
Task("CodeFileSanity")
.Does(() => {
ValidateCodeSanity(new ValidateCodeSanitySettings {
- RootDirectory = ".",
+ RootDirectory = rootDirectory.FullPath,
IsAppveyorBuild = AppVeyor.IsRunningOnAppVeyor
});
});
diff --git a/tools/cakebuild.csproj b/build/cakebuild.csproj
similarity index 100%
rename from tools/cakebuild.csproj
rename to build/cakebuild.csproj
diff --git a/fastlane/Appfile b/fastlane/Appfile
new file mode 100644
index 0000000000..083de66985
--- /dev/null
+++ b/fastlane/Appfile
@@ -0,0 +1,2 @@
+app_identifier("sh.ppy.osulazer") # The bundle identifier of your app
+apple_id("apple-dev@ppy.sh") # Your Apple email address
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
new file mode 100644
index 0000000000..3f64bcdf19
--- /dev/null
+++ b/fastlane/Fastfile
@@ -0,0 +1,65 @@
+update_fastlane
+
+default_platform(:ios)
+
+platform :ios do
+ lane :testflight_prune_dry do
+ clean_testflight_testers(days_of_inactivity:45, dry_run: true)
+ end
+
+ # Specify a custom number for what's "inactive"
+ lane :testflight_prune do
+ clean_testflight_testers(days_of_inactivity: 45) # 120 days, so about 4 months
+ end
+
+ lane :update_version do |options|
+ options[:plist_path] = '../osu.iOS/Info.plist'
+ app_version(options)
+ end
+
+ desc 'Deploy to testflight'
+ lane :beta do |options|
+ update_version(options)
+
+ provision(
+ type: 'appstore'
+ )
+
+ build(
+ build_configuration: 'Release',
+ build_platform: 'iPhone'
+ )
+
+ client = HTTPClient.new
+ changelog = client.get_content 'https://gist.githubusercontent.com/peppy/ab89c29dcc0dce95f39eb218e8fad197/raw'
+ changelog.gsub!('$BUILD_ID', options[:build])
+
+ pilot(
+ wait_processing_interval: 900,
+ changelog: changelog,
+ ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa'
+ )
+ end
+
+ desc 'Compile the project'
+ lane :build do
+ nuget_restore(
+ project_path: 'osu.iOS.sln'
+ )
+
+ souyuz(
+ platform: "ios",
+ build_target: "osu_iOS",
+ plist_path: "../osu.iOS/Info.plist"
+ )
+ end
+
+ desc 'Install provisioning profiles using match'
+ lane :provision do |options|
+ if Helper.is_ci?
+ options[:readonly] = true
+ end
+
+ match(options)
+ end
+end
diff --git a/fastlane/Matchfile b/fastlane/Matchfile
new file mode 100644
index 0000000000..40c974b09e
--- /dev/null
+++ b/fastlane/Matchfile
@@ -0,0 +1 @@
+git_url('https://github.com/peppy/apple-certificates')
diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile
new file mode 100644
index 0000000000..9f4f47f213
--- /dev/null
+++ b/fastlane/Pluginfile
@@ -0,0 +1,7 @@
+# Autogenerated by fastlane
+#
+# Ensure this file is checked in to source control!
+
+gem 'fastlane-plugin-clean_testflight_testers'
+gem 'fastlane-plugin-souyuz'
+gem 'fastlane-plugin-xamarin'
diff --git a/fastlane/README.md b/fastlane/README.md
new file mode 100644
index 0000000000..53bbc62cae
--- /dev/null
+++ b/fastlane/README.md
@@ -0,0 +1,54 @@
+fastlane documentation
+================
+# Installation
+
+Make sure you have the latest version of the Xcode command line tools installed:
+
+```
+xcode-select --install
+```
+
+Install _fastlane_ using
+```
+[sudo] gem install fastlane -NV
+```
+or alternatively using `brew cask install fastlane`
+
+# Available Actions
+## iOS
+### ios testflight_prune_dry
+```
+fastlane ios testflight_prune_dry
+```
+
+### ios testflight_prune
+```
+fastlane ios testflight_prune
+```
+
+### ios update_version
+```
+fastlane ios update_version
+```
+
+### ios beta
+```
+fastlane ios beta
+```
+Deploy to testflight
+### ios build
+```
+fastlane ios build
+```
+Compile the project
+### ios provision
+```
+fastlane ios provision
+```
+Install provisioning profiles using match
+
+----
+
+This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
+More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
+The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
diff --git a/osu-resources b/osu-resources
deleted file mode 160000
index 677897728f..0000000000
--- a/osu-resources
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 677897728f4332fa1200e0280ca02c4b987c6c47
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index c4c0cc1fdd..7e5b003f03 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -16,7 +16,6 @@ using osu.Desktop.Updater;
using osu.Framework;
using osu.Framework.Platform.Windows;
using osu.Framework.Screens;
-using osu.Game.Screens;
using osu.Game.Screens.Menu;
namespace osu.Desktop
@@ -63,9 +62,10 @@ namespace osu.Desktop
}
}
- protected override void ScreenChanged(OsuScreen current, Screen newScreen)
+ protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
{
- base.ScreenChanged(current, newScreen);
+ base.ScreenChanged(lastScreen, newScreen);
+
switch (newScreen)
{
case Intro _:
@@ -83,8 +83,7 @@ namespace osu.Desktop
public override void SetHost(GameHost host)
{
base.SetHost(host);
- var desktopWindow = host.Window as DesktopGameWindow;
- if (desktopWindow != null)
+ if (host.Window is DesktopGameWindow desktopWindow)
{
desktopWindow.CursorState |= CursorState.Hidden;
diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs
index 5b67d528ae..b8a0e337b6 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -60,7 +60,7 @@ namespace osu.Desktop.Overlays
{
new OsuSpriteText
{
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = game.Name
},
new OsuSpriteText
@@ -74,9 +74,8 @@ namespace osu.Desktop.Overlays
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- TextSize = 12,
+ Font = OsuFont.Numeric.With(size: 12),
Colour = colours.Yellow,
- Font = @"Venera",
Text = @"Development Build"
},
new Sprite
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 4f0ae3d65d..874f73da6d 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -22,7 +22,6 @@
-
diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj
index da053f7598..37e7c45a4e 100644
--- a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj
@@ -39,10 +39,6 @@
{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}
osu.Game.Rulesets.Catch
-
- {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}
- osu.Game.Resources
-
diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
index fd30a8d4ae..7f85d4ccce 100644
--- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
@@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[TestCase("basic")]
[TestCase("spinner")]
[TestCase("spinner-and-circles")]
+ [TestCase("slider")]
public new void Test(string name)
{
base.Test(name);
@@ -33,13 +34,16 @@ namespace osu.Game.Rulesets.Catch.Tests
case JuiceStream stream:
foreach (var nested in stream.NestedHitObjects)
yield return new ConvertValue((CatchHitObject)nested);
+
break;
case BananaShower shower:
foreach (var nested in shower.NestedHitObjects)
yield return new ConvertValue((CatchHitObject)nested);
+
break;
default:
yield return new ConvertValue((CatchHitObject)hitObject);
+
break;
}
}
diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs
new file mode 100644
index 0000000000..01c57a6b9a
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs
@@ -0,0 +1,24 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Catch.Difficulty;
+using osu.Game.Rulesets.Difficulty;
+using osu.Game.Tests.Beatmaps;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ public class CatchDifficultyCalculatorTest : DifficultyCalculatorTest
+ {
+ protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
+
+ [TestCase(4.2038001515546597d, "diffcalc-test")]
+ public void Test(double expected, string name)
+ => base.Test(expected, name);
+
+ protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap);
+
+ protected override Ruleset CreateRuleset() => new CatchRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs
index 030c52afea..8f9dd73b80 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchPlayer.cs
@@ -8,7 +8,8 @@ namespace osu.Game.Rulesets.Catch.Tests
[TestFixture]
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
{
- public TestCaseCatchPlayer() : base(new CatchRuleset())
+ public TestCaseCatchPlayer()
+ : base(new CatchRuleset())
{
}
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs
index 0851fbed87..7451986a8b 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs
@@ -1,9 +1,11 @@
// 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 NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.Catch.Tests
{
@@ -17,13 +19,30 @@ namespace osu.Game.Rulesets.Catch.Tests
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{
- var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
+ var beatmap = new Beatmap
+ {
+ BeatmapInfo =
+ {
+ Ruleset = ruleset.RulesetInfo,
+ BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f }
+ }
+ };
+
+ // Should produce a hperdash
+ beatmap.HitObjects.Add(new Fruit { StartTime = 816, X = 308 / 512f, NewCombo = true });
+ beatmap.HitObjects.Add(new Fruit { StartTime = 1008, X = 56 / 512f, });
for (int i = 0; i < 512; i++)
if (i % 5 < 3)
- beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
+ beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = 2000 + i * 100, NewCombo = i % 8 == 0 });
return beatmap;
}
+
+ protected override void AddCheckSteps(Func player)
+ {
+ base.AddCheckSteps(player);
+ AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
index 2932afa9fe..78b5a510b2 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
@@ -56,6 +56,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
rng.Next(); // osu!stable retrieved a random banana rotation
rng.Next(); // osu!stable retrieved a random banana colour
}
+
break;
case JuiceStream juiceStream:
foreach (var nested in juiceStream.NestedHitObjects)
@@ -67,6 +68,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
rng.Next(); // osu!stable retrieved a random droplet rotation
hitObject.X = MathHelper.Clamp(hitObject.X, 0, 1);
}
+
break;
}
}
@@ -98,7 +100,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
CatchHitObject nextObject = objectWithDroplets[i + 1];
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
- double timeToNext = nextObject.StartTime - currentObject.StartTime;
+ double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
if (distanceToHyper < 0)
diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs
index 285b90b0ce..021d7a7efe 100644
--- a/osu.Game.Rulesets.Catch/CatchInputManager.cs
+++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs
@@ -19,8 +19,10 @@ namespace osu.Game.Rulesets.Catch
{
[Description("Move left")]
MoveLeft,
+
[Description("Move right")]
MoveRight,
+
[Description("Engage dash")]
Dash,
}
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs
index c6518cbf8a..75f5b18607 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Difficulty;
-using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Difficulty
{
@@ -10,10 +9,5 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{
public double ApproachRate;
public int MaxCombo;
-
- public CatchDifficultyAttributes(Mod[] mods, double starRating)
- : base(mods, starRating)
- {
- }
}
}
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
index a0b813478d..8cfda5d532 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
@@ -1,148 +1,92 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Difficulty;
-using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Catch.Difficulty.Skills;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
+using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Difficulty
{
public class CatchDifficultyCalculator : DifficultyCalculator
{
- ///
- /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
- /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
- /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
- ///
- private const double strain_step = 750;
-
- ///
- /// The weighting of each strain value decays to this number * it's previous value
- ///
- private const double decay_weight = 0.94;
-
private const double star_scaling_factor = 0.145;
+ protected override int SectionLength => 750;
+
+ private readonly float halfCatchWidth;
+
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
+ var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty);
+ halfCatchWidth = catcher.CatchWidth * 0.5f;
+
+ // We're only using 80% of the catcher's width to simulate imperfect gameplay.
+ halfCatchWidth *= 0.8f;
}
- protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
+ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
- if (!beatmap.HitObjects.Any())
- return new CatchDifficultyAttributes(mods, 0);
+ if (beatmap.HitObjects.Count == 0)
+ return new CatchDifficultyAttributes { Mods = mods };
- var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty);
- float halfCatchWidth = catcher.CatchWidth * 0.5f;
+ // this is the same as osu!, so there's potential to share the implementation... maybe
+ double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
- var difficultyHitObjects = new List();
-
- foreach (var hitObject in beatmap.HitObjects)
+ return new CatchDifficultyAttributes
{
+ StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor,
+ Mods = mods,
+ ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
+ MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet))
+ };
+ }
+
+ protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
+ {
+ CatchHitObject lastObject = null;
+
+ foreach (var hitObject in beatmap.HitObjects.OfType())
+ {
+ if (lastObject == null)
+ {
+ lastObject = hitObject;
+ continue;
+ }
+
switch (hitObject)
{
// We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
case Fruit fruit:
- difficultyHitObjects.Add(new CatchDifficultyHitObject(fruit, halfCatchWidth));
+ yield return new CatchDifficultyHitObject(fruit, lastObject, clockRate, halfCatchWidth);
+
+ lastObject = hitObject;
break;
case JuiceStream _:
- difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
+ foreach (var nested in hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet)))
+ {
+ yield return new CatchDifficultyHitObject(nested, lastObject, clockRate, halfCatchWidth);
+
+ lastObject = nested;
+ }
+
break;
}
}
-
- difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
-
- if (!calculateStrainValues(difficultyHitObjects, timeRate))
- return new CatchDifficultyAttributes(mods, 0);
-
- // this is the same as osu!, so there's potential to share the implementation... maybe
- double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
- double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;
-
- return new CatchDifficultyAttributes(mods, starRating)
- {
- ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
- MaxCombo = difficultyHitObjects.Count
- };
}
- private bool calculateStrainValues(List objects, double timeRate)
+ protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
{
- CatchDifficultyHitObject lastObject = null;
-
- if (!objects.Any()) return false;
-
- // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
- foreach (var currentObject in objects)
- {
- if (lastObject != null)
- currentObject.CalculateStrains(lastObject, timeRate);
-
- lastObject = currentObject;
- }
-
- return true;
- }
-
- private double calculateDifficulty(List objects, double timeRate)
- {
- // The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods
- double actualStrainStep = strain_step * timeRate;
-
- // Find the highest strain value within each strain step
- var highestStrains = new List();
- double intervalEndTime = actualStrainStep;
- double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
-
- CatchDifficultyHitObject previousHitObject = null;
- foreach (CatchDifficultyHitObject hitObject in objects)
- {
- // While we are beyond the current interval push the currently available maximum to our strain list
- while (hitObject.BaseHitObject.StartTime > intervalEndTime)
- {
- highestStrains.Add(maximumStrain);
-
- // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
- // until the beginning of the next interval.
- if (previousHitObject == null)
- {
- maximumStrain = 0;
- }
- else
- {
- double decay = Math.Pow(CatchDifficultyHitObject.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
- maximumStrain = previousHitObject.Strain * decay;
- }
-
- // Go to the next time interval
- intervalEndTime += actualStrainStep;
- }
-
- // Obtain maximum strain
- maximumStrain = Math.Max(hitObject.Strain, maximumStrain);
-
- previousHitObject = hitObject;
- }
-
- // Build the weighted sum over the highest strains for each interval
- double difficulty = 0;
- double weight = 1;
- highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
-
- foreach (double strain in highestStrains)
- {
- difficulty += weight * strain;
- weight *= decay_weight;
- }
-
- return difficulty;
- }
+ new Movement(),
+ };
}
}
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs
deleted file mode 100644
index fb16e117b1..0000000000
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs
+++ /dev/null
@@ -1,130 +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.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Catch.UI;
-using osuTK;
-
-namespace osu.Game.Rulesets.Catch.Difficulty
-{
- public class CatchDifficultyHitObject
- {
- internal static readonly double DECAY_BASE = 0.20;
- private const float normalized_hitobject_radius = 41.0f;
- private const float absolute_player_positioning_error = 16f;
- private readonly float playerPositioningError;
-
- internal CatchHitObject BaseHitObject;
-
- ///
- /// Measures jump difficulty. CtB doesn't have something like button pressing speed or accuracy
- ///
- internal double Strain = 1;
-
- ///
- /// This is required to keep track of lazy player movement (always moving only as far as necessary)
- /// Without this quick repeat sliders / weirdly shaped streams might become ridiculously overrated
- ///
- internal float PlayerPositionOffset;
- internal float LastMovement;
-
- internal float NormalizedPosition;
- internal float ActualNormalizedPosition => NormalizedPosition + PlayerPositionOffset;
-
- internal CatchDifficultyHitObject(CatchHitObject baseHitObject, float catcherWidthHalf)
- {
- BaseHitObject = baseHitObject;
-
- // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
- float scalingFactor = normalized_hitobject_radius / catcherWidthHalf;
-
- playerPositioningError = absolute_player_positioning_error; // * scalingFactor;
- NormalizedPosition = baseHitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
- }
-
- private const double direction_change_bonus = 12.5;
- internal void CalculateStrains(CatchDifficultyHitObject previousHitObject, double timeRate)
- {
- // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make.
- // See Taiko feedback thread.
- double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
- double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000);
-
- // Update new position with lazy movement.
- PlayerPositionOffset =
- MathHelper.Clamp(
- previousHitObject.ActualNormalizedPosition,
- NormalizedPosition - (normalized_hitobject_radius - playerPositioningError),
- NormalizedPosition + (normalized_hitobject_radius - playerPositioningError)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player.
- - NormalizedPosition; // Subtract HitObject position to obtain offset
-
- LastMovement = DistanceTo(previousHitObject);
- double addition = spacingWeight(LastMovement);
-
- if (NormalizedPosition < previousHitObject.NormalizedPosition)
- {
- LastMovement = -LastMovement;
- }
-
- CatchHitObject previousHitCircle = previousHitObject.BaseHitObject;
-
- double additionBonus = 0;
- double sqrtTime = Math.Sqrt(Math.Max(timeElapsed, 25));
-
- // Direction changes give an extra point!
- if (Math.Abs(LastMovement) > 0.1)
- {
- if (Math.Abs(previousHitObject.LastMovement) > 0.1 && Math.Sign(LastMovement) != Math.Sign(previousHitObject.LastMovement))
- {
- double bonus = direction_change_bonus / sqrtTime;
-
- // Weight bonus by how
- double bonusFactor = Math.Min(playerPositioningError, Math.Abs(LastMovement)) / playerPositioningError;
-
- // We want time to play a role twice here!
- addition += bonus * bonusFactor;
-
- // Bonus for tougher direction switches and "almost" hyperdashes at this point
- if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
- {
- additionBonus += 0.3 * bonusFactor;
- }
- }
-
- // Base bonus for every movement, giving some weight to streams.
- addition += 7.5 * Math.Min(Math.Abs(LastMovement), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtTime;
- }
-
- // Bonus for "almost" hyperdashes at corner points
- if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
- {
- if (!previousHitCircle.HyperDash)
- {
- additionBonus += 1.0;
- }
- else
- {
- // After a hyperdash we ARE in the correct position. Always!
- PlayerPositionOffset = 0;
- }
-
- addition *= 1.0 + additionBonus * ((10 - previousHitCircle.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10);
- }
-
- addition *= 850.0 / Math.Max(timeElapsed, 25);
-
- Strain = previousHitObject.Strain * decay + addition;
- }
-
- private static double spacingWeight(float distance)
- {
- return Math.Pow(distance, 1.3) / 500;
- }
-
- internal float DistanceTo(CatchDifficultyHitObject other)
- {
- return Math.Abs(ActualNormalizedPosition - other.ActualNormalizedPosition);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs
new file mode 100644
index 0000000000..24e526ed19
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs
@@ -0,0 +1,41 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Objects;
+
+namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
+{
+ public class CatchDifficultyHitObject : DifficultyHitObject
+ {
+ private const float normalized_hitobject_radius = 41.0f;
+
+ public new CatchHitObject BaseObject => (CatchHitObject)base.BaseObject;
+
+ public new CatchHitObject LastObject => (CatchHitObject)base.LastObject;
+
+ public readonly float NormalizedPosition;
+ public readonly float LastNormalizedPosition;
+
+ ///
+ /// Milliseconds elapsed since the start time of the previous , with a minimum of 25ms.
+ ///
+ public readonly double StrainTime;
+
+ public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
+ : base(hitObject, lastObject, clockRate)
+ {
+ // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
+ var scalingFactor = normalized_hitobject_radius / halfCatcherWidth;
+
+ NormalizedPosition = BaseObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
+ LastNormalizedPosition = LastObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
+
+ // Every strain interval is hard capped at the equivalent of 600 BPM streaming speed as a safety measure
+ StrainTime = Math.Max(25, DeltaTime);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs
new file mode 100644
index 0000000000..d146153294
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs
@@ -0,0 +1,85 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Difficulty.Skills
+{
+ public class Movement : Skill
+ {
+ private const float absolute_player_positioning_error = 16f;
+ private const float normalized_hitobject_radius = 41.0f;
+ private const double direction_change_bonus = 12.5;
+
+ protected override double SkillMultiplier => 850;
+ protected override double StrainDecayBase => 0.2;
+
+ protected override double DecayWeight => 0.94;
+
+ private float? lastPlayerPosition;
+ private float lastDistanceMoved;
+
+ protected override double StrainValueOf(DifficultyHitObject current)
+ {
+ var catchCurrent = (CatchDifficultyHitObject)current;
+
+ if (lastPlayerPosition == null)
+ lastPlayerPosition = catchCurrent.LastNormalizedPosition;
+
+ float playerPosition = MathHelper.Clamp(
+ lastPlayerPosition.Value,
+ catchCurrent.NormalizedPosition - (normalized_hitobject_radius - absolute_player_positioning_error),
+ catchCurrent.NormalizedPosition + (normalized_hitobject_radius - absolute_player_positioning_error)
+ );
+
+ float distanceMoved = playerPosition - lastPlayerPosition.Value;
+
+ double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 500;
+ double sqrtStrain = Math.Sqrt(catchCurrent.StrainTime);
+
+ double bonus = 0;
+
+ // Direction changes give an extra point!
+ if (Math.Abs(distanceMoved) > 0.1)
+ {
+ if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved))
+ {
+ double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(distanceMoved)) / absolute_player_positioning_error;
+
+ distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor;
+
+ // Bonus for tougher direction switches and "almost" hyperdashes at this point
+ if (catchCurrent.LastObject.DistanceToHyperDash <= 10 / CatchPlayfield.BASE_WIDTH)
+ bonus = 0.3 * bonusFactor;
+ }
+
+ // Base bonus for every movement, giving some weight to streams.
+ distanceAddition += 7.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain;
+ }
+
+ // Bonus for "almost" hyperdashes at corner points
+ if (catchCurrent.LastObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
+ {
+ if (!catchCurrent.LastObject.HyperDash)
+ bonus += 1.0;
+ else
+ {
+ // After a hyperdash we ARE in the correct position. Always!
+ playerPosition = catchCurrent.NormalizedPosition;
+ }
+
+ distanceAddition *= 1.0 + bonus * ((10 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10);
+ }
+
+ lastPlayerPosition = playerPosition;
+ lastDistanceMoved = distanceMoved;
+
+ return distanceAddition / catchCurrent.StrainTime;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
index 2bd2c9766f..b3605b013b 100644
--- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
+++ b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
@@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils
/// The random value.
public uint NextUInt()
{
- uint t = _x ^ _x << 11;
+ uint t = _x ^ (_x << 11);
_x = _y;
_y = _z;
_z = _w;
- return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
+ return _w = _w ^ (_w >> 19) ^ t ^ (t >> 8);
}
///
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs
index 4f5d7abfd4..82cda7df47 100644
--- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.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.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
@@ -55,9 +56,9 @@ namespace osu.Game.Rulesets.Catch.Mods
return default_flashlight_size;
}
- protected override void OnComboChange(int newCombo)
+ protected override void OnComboChange(ValueChangedEvent e)
{
- this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(newCombo)), FLASHLIGHT_FADE_DURATION);
+ this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION);
}
protected override string FragmentShader => "CircularFlashlight";
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
index 922d6bf3e9..294fd97d59 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
- AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
+ AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
private const float preempt = 1000;
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
index c80dacde93..8fed8eb4cd 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
public override Color4 AccentColour
{
- get { return base.AccentColour; }
+ get => base.AccentColour;
set
{
base.AccentColour = value;
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
index d5adbee8aa..2e18c5f2ad 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
@@ -23,9 +23,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
}
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
accentColour = value;
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index ef7573c70b..61bb4335f3 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -55,6 +55,13 @@ namespace osu.Game.Rulesets.Catch.Objects
var minDistanceFromEnd = Velocity * 0.01;
+ var tickSamples = Samples.Select(s => new SampleInfo
+ {
+ Bank = s.Bank,
+ Name = @"slidertick",
+ Volume = s.Volume
+ }).ToList();
+
AddNested(new Fruit
{
Samples = Samples,
@@ -62,15 +69,22 @@ namespace osu.Game.Rulesets.Catch.Objects
X = X
});
- double lastDropletTime = StartTime;
+ double lastTickTime = StartTime;
for (int span = 0; span < this.SpanCount(); span++)
{
var spanStartTime = StartTime + span * spanDuration;
var reversed = span % 2 == 1;
- for (double d = 0; d <= length; d += tickDistance)
+ for (double d = tickDistance;; d += tickDistance)
{
+ bool isLastTick = false;
+ if (d + minDistanceFromEnd >= length)
+ {
+ d = length;
+ isLastTick = true;
+ }
+
var timeProgress = d / length;
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
@@ -79,47 +93,42 @@ namespace osu.Game.Rulesets.Catch.Objects
if (LegacyLastTickOffset != null)
{
// If we're the last tick, apply the legacy offset
- if (span == this.SpanCount() - 1 && d + tickDistance > length)
+ if (span == this.SpanCount() - 1 && isLastTick)
time = Math.Max(StartTime + Duration / 2, time - LegacyLastTickOffset.Value);
}
- double tinyTickInterval = time - lastDropletTime;
- while (tinyTickInterval > 100)
- tinyTickInterval /= 2;
-
- for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
+ int tinyTickCount = 1;
+ double tinyTickInterval = time - lastTickTime;
+ while (tinyTickInterval > 100 && tinyTickCount < 10000)
{
+ tinyTickInterval /= 2;
+ tinyTickCount *= 2;
+ }
+
+ for (int tinyTickIndex = 0; tinyTickIndex < tinyTickCount - 1; tinyTickIndex++)
+ {
+ var t = lastTickTime + (tinyTickIndex + 1) * tinyTickInterval;
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
AddNested(new TinyDroplet
{
StartTime = t,
X = X + Path.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
- Samples = new List(Samples.Select(s => new SampleInfo
- {
- Bank = s.Bank,
- Name = @"slidertick",
- Volume = s.Volume
- }))
+ Samples = tickSamples
});
}
- if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
+ lastTickTime = time;
+
+ if (isLastTick)
+ break;
+
+ AddNested(new Droplet
{
- AddNested(new Droplet
- {
- StartTime = time,
- X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
- Samples = new List(Samples.Select(s => new SampleInfo
- {
- Bank = s.Bank,
- Name = @"slidertick",
- Volume = s.Volume
- }))
- });
- }
-
- lastDropletTime = time;
+ StartTime = time,
+ X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
+ Samples = tickSamples
+ });
}
AddNested(new Fruit
diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/diffcalc-test.osu
new file mode 100644
index 0000000000..ebad654404
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/diffcalc-test.osu
@@ -0,0 +1,138 @@
+osu file format v14
+
+[General]
+StackLeniency: 0.3
+Mode: 2
+
+[Difficulty]
+CircleSize:4
+OverallDifficulty:7
+ApproachRate:8.3
+SliderMultiplier:1.6
+SliderTickRate:1
+
+[TimingPoints]
+500,500,4,2,1,50,1,0
+34500,-50,4,2,1,50,0,0
+
+[HitObjects]
+// fruits spaced 1/1 beat apart
+32,128,0,5,0,0:0:0:0:
+96,128,500,1,0,0:0:0:0:
+160,128,1000,1,0,0:0:0:0:
+224,128,1500,1,0,0:0:0:0:
+288,128,2000,1,0,0:0:0:0:
+352,128,2500,1,0,0:0:0:0:
+416,128,3000,1,0,0:0:0:0:
+480,128,3500,1,0,0:0:0:0:
+
+// fruits spaced 1/2 beat apart
+32,160,4500,1,0,0:0:0:0:
+64,160,4750,1,0,0:0:0:0:
+96,160,5000,1,0,0:0:0:0:
+128,160,5250,1,0,0:0:0:0:
+160,160,5500,1,0,0:0:0:0:
+192,160,5750,1,0,0:0:0:0:
+224,160,6000,1,0,0:0:0:0:
+256,160,6250,1,0,0:0:0:0:
+288,160,6500,1,0,0:0:0:0:
+
+// fruits spaced 1/4 beat apart
+96,128,7500,1,0,0:0:0:0:
+128,128,7625,1,0,0:0:0:0:
+160,128,7750,1,0,0:0:0:0:
+192,128,7875,1,0,0:0:0:0:
+224,128,8000,1,0,0:0:0:0:
+256,128,8125,1,0,0:0:0:0:
+288,128,8250,1,0,0:0:0:0:
+320,128,8375,1,0,0:0:0:0:
+352,128,8500,1,0,0:0:0:0:
+
+// fruit hyperdashes, spaced 1/2 beat apart
+32,160,9500,1,0,0:0:0:0:
+480,160,9750,1,0,0:0:0:0:
+32,160,10000,1,0,0:0:0:0:
+480,160,10250,1,0,0:0:0:0:
+32,160,10500,1,0,0:0:0:0:
+480,160,10750,1,0,0:0:0:0:
+32,160,11000,1,0,0:0:0:0:
+
+// fruit hyperdashes, spaced 1/4 beat apart
+32,192,12000,1,0,0:0:0:0:
+480,192,12125,1,0,0:0:0:0:
+32,192,12250,1,0,0:0:0:0:
+480,192,12375,1,0,0:0:0:0:
+32,192,12500,1,0,0:0:0:0:
+480,192,12625,1,0,0:0:0:0:
+32,192,12750,1,0,0:0:0:0:
+480,192,12875,1,0,0:0:0:0:
+32,192,13000,1,0,0:0:0:0:
+
+// stream + hyperdash + stream, spaced 1/4 beat apart
+32,192,14000,1,0,0:0:0:0:
+64,192,14125,1,0,0:0:0:0:
+96,192,14250,1,0,0:0:0:0:
+128,192,14375,1,0,0:0:0:0:
+480,192,14500,1,0,0:0:0:0:
+448,192,14625,1,0,0:0:0:0:
+416,192,14750,1,0,0:0:0:0:
+384,192,14875,1,0,0:0:0:0:
+32,192,15000,1,0,0:0:0:0:
+
+// basic sliders
+32,192,16000,2,0,L|192:192,1,160
+224,192,17000,2,0,L|384:192,1,160
+416,192,17875,2,0,L|480:192,1,40
+
+// slider hyperdashes, spaced 1/4 beat apart
+32,192,19000,2,0,L|128:192,1,80
+480,192,19375,2,0,L|384:192,1,80
+352,192,19750,2,0,L|256:192,1,80
+0,192,20125,2,0,L|128:192,1,120
+
+// stream + slider hyperdashes, spaced 1/4 beat apart
+32,192,21500,1,0,0:0:0:0:
+64,192,21625,1,0,0:0:0:0:
+96,192,21750,1,0,0:0:0:0:
+512,192,21875,2,0,L|320:192,1,160
+320,192,22500,1,0,0:0:0:0:
+288,192,22625,1,0,0:0:0:0:
+256,192,22750,1,0,0:0:0:0:
+0,192,22875,2,0,L|64:192,1,40
+
+// streams, spaced 1/4 beat apart
+64,192,24000,1,0,0:0:0:0:
+160,192,24125,1,0,0:0:0:0:
+64,192,24250,1,0,0:0:0:0:
+160,192,24375,1,0,0:0:0:0:
+64,192,24500,1,0,0:0:0:0:
+160,192,24625,1,0,0:0:0:0:
+64,192,24750,1,0,0:0:0:0:
+160,192,24875,1,0,0:0:0:0:
+64,192,25000,1,0,0:0:0:0:
+160,192,25125,1,0,0:0:0:0:
+64,192,25250,1,0,0:0:0:0:
+160,192,25375,1,0,0:0:0:0:
+64,192,25500,1,0,0:0:0:0:
+
+// stream + spinner combo, spaced 1/4 beat apart
+256,192,26500,12,0,27000,0:0:0:0:
+128,192,27250,5,0,0:0:0:0:
+128,192,27375,1,0,0:0:0:0:
+160,192,27500,1,0,0:0:0:0:
+192,192,27625,1,0,0:0:0:0:
+256,192,27750,12,0,28500,0:0:0:0:
+192,192,28625,5,0,0:0:0:0:
+224,192,28750,1,0,0:0:0:0:
+256,192,28875,1,0,0:0:0:0:
+256,192,29000,1,0,0:0:0:0:
+256,192,29125,12,0,29500,0:0:0:0:
+
+// long slow slider
+0,192,30500,6,0,B|480:192|480:192|0:192,2,960
+
+// long fast slider
+0,192,37500,6,0,B|480:192|480:192|0:192,2,960
+
+// long hyperdash slider
+0,192,41500,2,0,P|544:192|544:192,5,480
diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider-expected-conversion.json
new file mode 100644
index 0000000000..58c52b6867
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider-expected-conversion.json
@@ -0,0 +1 @@
+{"Mappings":[{"StartTime":19184.0,"Objects":[{"StartTime":19184.0,"Position":320.0},{"StartTime":19263.0,"Position":311.730255},{"StartTime":19343.0,"Position":324.6205},{"StartTime":19423.0,"Position":343.0907},{"StartTime":19503.0,"Position":372.2917},{"StartTime":19582.0,"Position":385.194733},{"StartTime":19662.0,"Position":379.0426},{"StartTime":19742.0,"Position":385.1066},{"StartTime":19822.0,"Position":391.624664},{"StartTime":19919.0,"Position":386.27832},{"StartTime":20016.0,"Position":380.117035},{"StartTime":20113.0,"Position":381.664154},{"StartTime":20247.0,"Position":370.872864}]}]}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider.osu
new file mode 100644
index 0000000000..d48b2d7769
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider.osu
@@ -0,0 +1,18 @@
+osu file format v14
+
+[General]
+Mode: 2
+
+[Difficulty]
+HPDrainRate:3
+CircleSize:2
+OverallDifficulty:4
+ApproachRate:4
+SliderMultiplier:0.9
+SliderTickRate:1
+
+[TimingPoints]
+35.4473684210527,638.298947368422,4,2,1,60,1,0
+
+[HitObjects]
+320,176,19184,2,8,P|384:168|368:232,1,150
diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 5e33dd59b1..e1fda1a7b3 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -43,6 +43,6 @@ namespace osu.Game.Rulesets.Catch.Scoring
Health.Value += Math.Max(result.Judgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
}
- protected override HitWindows CreateHitWindows() => new CatchHitWindows();
+ public override HitWindows CreateHitWindows() => new CatchHitWindows();
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index d79f106310..d0f50c6af2 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
public class CatcherArea : Container
{
- public const float CATCHER_SIZE = 100;
+ public const float CATCHER_SIZE = 106.75f;
protected internal readonly Catcher MovableCatcher;
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.UI
public Container ExplodingFruitTarget
{
- set { MovableCatcher.ExplodingFruitTarget = value; }
+ set => MovableCatcher.ExplodingFruitTarget = value;
}
public CatcherArea(BeatmapDifficulty difficulty = null)
@@ -158,7 +158,7 @@ namespace osu.Game.Rulesets.Catch.UI
protected bool Dashing
{
- get { return dashing; }
+ get => dashing;
set
{
if (value == dashing) return;
@@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Catch.UI
///
protected bool Trail
{
- get { return trail; }
+ get => trail;
set
{
if (value == trail) return;
diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj
index 45ed2091a7..24abccb19d 100644
--- a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj
@@ -39,10 +39,6 @@
{48F4582B-7687-4621-9CBE-5C24197CB536}
osu.Game.Rulesets.Mania
-
- {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}
- osu.Game.Resources
-
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs
index 7a2b4e7b39..6b95975059 100644
--- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs
@@ -40,29 +40,29 @@ namespace osu.Game.Rulesets.Mania.Tests
protected override Ruleset CreateRuleset() => new ManiaRuleset();
}
- public class ManiaConvertMapping : ConvertMapping, IEquatable
- {
- public uint RandomW;
- public uint RandomX;
- public uint RandomY;
- public uint RandomZ;
+ public class ManiaConvertMapping : ConvertMapping, IEquatable
+ {
+ public uint RandomW;
+ public uint RandomX;
+ public uint RandomY;
+ public uint RandomZ;
- public ManiaConvertMapping()
- {
- }
+ public ManiaConvertMapping()
+ {
+ }
- public ManiaConvertMapping(IBeatmapConverter converter)
- {
- var maniaConverter = (ManiaBeatmapConverter)converter;
- RandomW = maniaConverter.Random.W;
- RandomX = maniaConverter.Random.X;
- RandomY = maniaConverter.Random.Y;
- RandomZ = maniaConverter.Random.Z;
- }
+ public ManiaConvertMapping(IBeatmapConverter converter)
+ {
+ var maniaConverter = (ManiaBeatmapConverter)converter;
+ RandomW = maniaConverter.Random.W;
+ RandomX = maniaConverter.Random.X;
+ RandomY = maniaConverter.Random.Y;
+ RandomZ = maniaConverter.Random.Z;
+ }
- public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ;
- public override bool Equals(ConvertMapping other) => base.Equals(other) && Equals(other as ManiaConvertMapping);
- }
+ public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ;
+ public override bool Equals(ConvertMapping other) => base.Equals(other) && Equals(other as ManiaConvertMapping);
+ }
public struct ConvertValue : IEquatable
{
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs
new file mode 100644
index 0000000000..2c36e81190
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs
@@ -0,0 +1,24 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Mania.Difficulty;
+using osu.Game.Tests.Beatmaps;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ public class ManiaDifficultyCalculatorTest : DifficultyCalculatorTest
+ {
+ protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
+
+ [TestCase(2.3683365342338796d, "diffcalc-test")]
+ public void Test(double expected, string name)
+ => base.Test(expected, name);
+
+ protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap);
+
+ protected override Ruleset CreateRuleset() => new ManiaRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs
index 32f455bb73..e721eb6fd9 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs
@@ -3,7 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Tests.Visual;
@@ -25,8 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests
[BackgroundDependencyLoader]
private void load(RulesetConfigCache configCache)
{
- var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
- config.BindWith(ManiaSetting.ScrollDirection, direction);
+ var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
+ config.BindWith(ManiaRulesetSetting.ScrollDirection, direction);
}
}
}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
index 2f4b51e372..0bc2c3ea28 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
@@ -141,7 +142,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
- TextSize = 14,
+ Font = OsuFont.GetFont(size: 14),
Text = description
}
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 380ce533bb..71df68c087 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -27,6 +27,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
public int TargetColumns;
+ public bool Dual;
public readonly bool IsForCurrentRuleset;
// Internal for testing purposes
@@ -45,7 +46,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
if (IsForCurrentRuleset)
+ {
TargetColumns = (int)Math.Max(1, roundedCircleSize);
+ if (TargetColumns >= 10)
+ {
+ TargetColumns = TargetColumns / 2;
+ Dual = true;
+ }
+ }
else
{
float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count;
@@ -70,14 +78,22 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
return base.ConvertBeatmap(original);
}
- protected override Beatmap CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns });
+ protected override Beatmap CreateBeatmap()
+ {
+ beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns });
+
+ if (Dual)
+ beatmap.Stages.Add(new StageDefinition { Columns = TargetColumns });
+
+ return beatmap;
+ }
protected override IEnumerable ConvertHitObject(HitObject original, IBeatmap beatmap)
{
- var maniaOriginal = original as ManiaHitObject;
- if (maniaOriginal != null)
+ if (original is ManiaHitObject maniaOriginal)
{
yield return maniaOriginal;
+
yield break;
}
@@ -92,6 +108,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private readonly List prevNoteTimes = new List(max_notes_for_density);
private double density = int.MaxValue;
+
private void computeDensity(double newNoteTime)
{
if (prevNoteTimes.Count == max_notes_for_density)
@@ -104,6 +121,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private double lastTime;
private Vector2 lastPosition;
private PatternType lastStair = PatternType.Stair;
+
private void recordNote(double time, Vector2 position)
{
lastTime = time;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 3eff2d62f3..ed52bdd23f 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -65,6 +65,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (originalPattern.HitObjects.Count() == 1)
{
yield return originalPattern;
+
yield break;
}
@@ -135,6 +136,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.78, 0.3, 0);
+
return generateNRandomNotes(HitObject.StartTime, 0.85, 0.36, 0.03);
}
@@ -142,6 +144,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.43, 0.08, 0);
+
return generateNRandomNotes(HitObject.StartTime, 0.56, 0.18, 0);
}
@@ -149,11 +152,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.3, 0, 0);
+
return generateNRandomNotes(HitObject.StartTime, 0.37, 0.08, 0);
}
if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.17, 0, 0);
+
return generateNRandomNotes(HitObject.StartTime, 0.27, 0, 0);
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index 7004ea4969..34f5f5c415 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -116,10 +116,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
}
if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
- // If we convert to 7K + 1, let's not overload the special key
- && (TotalColumns != 8 || lastColumn != 0)
- // Make sure the last column was not the centre column
- && (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
+ // If we convert to 7K + 1, let's not overload the special key
+ && (TotalColumns != 8 || lastColumn != 0)
+ // Make sure the last column was not the centre column
+ && (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
{
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
int column = RandomStart + TotalColumns - lastColumn - 1;
@@ -172,6 +172,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return pattern = generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
if (ConversionDifficulty > 4)
return pattern = generateRandomPatternWithMirrored(0.12, 0.17, 0);
+
return pattern = generateRandomPatternWithMirrored(0.12, 0, 0);
}
@@ -179,6 +180,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
if (convertType.HasFlag(PatternType.LowProbability))
return pattern = generateRandomPattern(0.78, 0.42, 0, 0);
+
return pattern = generateRandomPattern(1, 0.62, 0, 0);
}
@@ -186,6 +188,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
if (convertType.HasFlag(PatternType.LowProbability))
return pattern = generateRandomPattern(0.35, 0.08, 0, 0);
+
return pattern = generateRandomPattern(0.52, 0.15, 0, 0);
}
@@ -193,6 +196,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
if (convertType.HasFlag(PatternType.LowProbability))
return pattern = generateRandomPattern(0.18, 0, 0, 0);
+
return pattern = generateRandomPattern(0.45, 0, 0, 0);
}
@@ -250,6 +254,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
}
else
last = GetRandomColumn();
+
return last;
}
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
index cf5dc4fe66..b702291c5d 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
@@ -87,6 +87,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return 4;
if (val >= 1 - p3)
return 3;
+
return val >= 1 - p2 ? 2 : 1;
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs
index a6fa234960..a3cd455886 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs
@@ -12,51 +12,63 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
internal enum PatternType
{
None = 0,
+
///
/// Keep the same as last row.
///
ForceStack = 1 << 0,
+
///
/// Keep different from last row.
///
ForceNotStack = 1 << 1,
+
///
/// Keep as single note at its original position.
///
KeepSingle = 1 << 2,
+
///
/// Use a lower random value.
///
LowProbability = 1 << 3,
+
///
/// Reserved.
///
Alternate = 1 << 4,
+
///
/// Ignore the repeat count.
///
ForceSigSlider = 1 << 5,
+
///
/// Convert slider to circle.
///
ForceNotSlider = 1 << 6,
+
///
/// Notes gathered together.
///
Gathered = 1 << 7,
Mirror = 1 << 8,
+
///
/// Change 0 -> 6.
///
Reverse = 1 << 9,
+
///
/// 1 -> 5 -> 1 -> 5 like reverse.
///
Cycle = 1 << 10,
+
///
/// Next note will be at column + 1.
///
Stair = 1 << 11,
+
///
/// Next note will be at column - 1.
///
diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
similarity index 57%
rename from osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
rename to osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
index 4e0ad31105..b591f9da22 100644
--- a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
+++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
@@ -8,9 +8,9 @@ using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Configuration
{
- public class ManiaConfigManager : RulesetConfigManager
+ public class ManiaRulesetConfigManager : RulesetConfigManager
{
- public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
+ public ManiaRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
: base(settings, ruleset, variant)
{
}
@@ -19,17 +19,17 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
base.InitialiseDefaults();
- Set(ManiaSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
- Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down);
+ Set(ManiaRulesetSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
+ Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{
- new TrackedSetting(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
+ new TrackedSetting(ManiaRulesetSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
};
}
- public enum ManiaSetting
+ public enum ManiaRulesetSetting
{
ScrollTime,
ScrollDirection
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs
index 2f614ea14b..3ff665d2c8 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs
@@ -2,17 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Difficulty;
-using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Difficulty
{
public class ManiaDifficultyAttributes : DifficultyAttributes
{
public double GreatHitWindow;
-
- public ManiaDifficultyAttributes(Mod[] mods, double starRating)
- : base(mods, starRating)
- {
- }
}
}
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
index 5b6fd4ecf3..59fed1031f 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
@@ -1,34 +1,24 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Mania.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Mods;
-using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Difficulty
{
- internal class ManiaDifficultyCalculator : DifficultyCalculator
+ public class ManiaDifficultyCalculator : DifficultyCalculator
{
private const double star_scaling_factor = 0.018;
- ///
- /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step.
- /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
- /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
- ///
- private const double strain_step = 400;
-
- ///
- /// The weighting of each strain value decays to this number * it's previous value
- ///
- private const double decay_weight = 0.9;
-
private readonly bool isForCurrentRuleset;
public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
@@ -37,108 +27,70 @@ namespace osu.Game.Rulesets.Mania.Difficulty
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
}
- protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
+ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
- if (!beatmap.HitObjects.Any())
- return new ManiaDifficultyAttributes(mods, 0);
+ if (beatmap.HitObjects.Count == 0)
+ return new ManiaDifficultyAttributes { Mods = mods };
- var difficultyHitObjects = new List();
-
- int columnCount = ((ManiaBeatmap)beatmap).TotalColumns;
-
- // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
- // Note: Stable sort is done so that the ordering of hitobjects with equal start times doesn't change
- difficultyHitObjects.AddRange(beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime));
-
- if (!calculateStrainValues(difficultyHitObjects, timeRate))
- return new DifficultyAttributes(mods, 0);
-
- double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
-
- return new ManiaDifficultyAttributes(mods, starRating)
+ return new ManiaDifficultyAttributes
{
- // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
- GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate
+ StarRating = difficultyValue(skills) * star_scaling_factor,
+ Mods = mods,
+ // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
+ GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
};
}
- private bool calculateStrainValues(List objects, double timeRate)
+ private double difficultyValue(Skill[] skills)
{
- // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
- using (var hitObjectsEnumerator = objects.GetEnumerator())
+ // Preprocess the strains to find the maximum overall + individual (aggregate) strain from each section
+ var overall = skills.OfType().Single();
+ var aggregatePeaks = new List(Enumerable.Repeat(0.0, overall.StrainPeaks.Count));
+
+ foreach (var individual in skills.OfType())
{
- if (!hitObjectsEnumerator.MoveNext())
- return false;
-
- ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current;
-
- // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
- while (hitObjectsEnumerator.MoveNext())
+ for (int i = 0; i < individual.StrainPeaks.Count; i++)
{
- var next = hitObjectsEnumerator.Current;
- next?.CalculateStrains(current, timeRate);
- current = next;
+ double aggregate = individual.StrainPeaks[i] + overall.StrainPeaks[i];
+
+ if (aggregate > aggregatePeaks[i])
+ aggregatePeaks[i] = aggregate;
}
-
- return true;
- }
- }
-
- private double calculateDifficulty(List objects, double timeRate)
- {
- double actualStrainStep = strain_step * timeRate;
-
- // Find the highest strain value within each strain step
- List highestStrains = new List();
- double intervalEndTime = actualStrainStep;
- double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
-
- ManiaHitObjectDifficulty previousHitObject = null;
- foreach (var hitObject in objects)
- {
- // While we are beyond the current interval push the currently available maximum to our strain list
- while (hitObject.BaseHitObject.StartTime > intervalEndTime)
- {
- highestStrains.Add(maximumStrain);
-
- // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
- // until the beginning of the next interval.
- if (previousHitObject == null)
- {
- maximumStrain = 0;
- }
- else
- {
- double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
- double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
- maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay;
- }
-
- // Go to the next time interval
- intervalEndTime += actualStrainStep;
- }
-
- // Obtain maximum strain
- double strain = hitObject.IndividualStrain + hitObject.OverallStrain;
- maximumStrain = Math.Max(strain, maximumStrain);
-
- previousHitObject = hitObject;
}
- // Build the weighted sum over the highest strains for each interval
+ aggregatePeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
+
double difficulty = 0;
double weight = 1;
- highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
- foreach (double strain in highestStrains)
+ // Difficulty is the weighted sum of the highest strains from every section.
+ foreach (double strain in aggregatePeaks)
{
- difficulty += weight * strain;
- weight *= decay_weight;
+ difficulty += strain * weight;
+ weight *= 0.9;
}
return difficulty;
}
+ protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
+ {
+ for (int i = 1; i < beatmap.HitObjects.Count; i++)
+ yield return new ManiaDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate);
+ }
+
+ protected override Skill[] CreateSkills(IBeatmap beatmap)
+ {
+ int columnCount = ((ManiaBeatmap)beatmap).TotalColumns;
+
+ var skills = new List { new Overall(columnCount) };
+
+ for (int i = 0; i < columnCount; i++)
+ skills.Add(new Individual(i, columnCount));
+
+ return skills.ToArray();
+ }
+
protected override Mod[] DifficultyAdjustmentMods
{
get
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs
index 2f4870f647..b99bddee96 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs
@@ -114,8 +114,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty
// Lots of arbitrary values from testing.
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
double accuracyValue = Math.Max(0.0, 0.2 - (Attributes.GreatHitWindow - 34) * 0.006667)
- * strainValue
- * Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
+ * strainValue
+ * Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
// accuracyValue *= Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
diff --git a/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs b/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs
new file mode 100644
index 0000000000..29ba934e9f
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs
@@ -0,0 +1,19 @@
+// 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.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Objects;
+
+namespace osu.Game.Rulesets.Mania.Difficulty.Preprocessing
+{
+ public class ManiaDifficultyHitObject : DifficultyHitObject
+ {
+ public new ManiaHitObject BaseObject => (ManiaHitObject)base.BaseObject;
+
+ public ManiaDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate)
+ : base(hitObject, lastObject, clockRate)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs
new file mode 100644
index 0000000000..059cd39641
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs
@@ -0,0 +1,47 @@
+// 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.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
+using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Mania.Objects;
+
+namespace osu.Game.Rulesets.Mania.Difficulty.Skills
+{
+ public class Individual : Skill
+ {
+ protected override double SkillMultiplier => 1;
+ protected override double StrainDecayBase => 0.125;
+
+ private readonly double[] holdEndTimes;
+
+ private readonly int column;
+
+ public Individual(int column, int columnCount)
+ {
+ this.column = column;
+
+ holdEndTimes = new double[columnCount];
+ }
+
+ protected override double StrainValueOf(DifficultyHitObject current)
+ {
+ var maniaCurrent = (ManiaDifficultyHitObject)current;
+ var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime;
+
+ try
+ {
+ if (maniaCurrent.BaseObject.Column != column)
+ return 0;
+
+ // We give a slight bonus if something is held meanwhile
+ return holdEndTimes.Any(t => t > endTime) ? 2.5 : 2;
+ }
+ finally
+ {
+ holdEndTimes[maniaCurrent.BaseObject.Column] = endTime;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs
new file mode 100644
index 0000000000..ed25173d38
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.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 osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
+using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Mania.Objects;
+
+namespace osu.Game.Rulesets.Mania.Difficulty.Skills
+{
+ public class Overall : Skill
+ {
+ protected override double SkillMultiplier => 1;
+ protected override double StrainDecayBase => 0.3;
+
+ private readonly double[] holdEndTimes;
+
+ private readonly int columnCount;
+
+ public Overall(int columnCount)
+ {
+ this.columnCount = columnCount;
+
+ holdEndTimes = new double[columnCount];
+ }
+
+ protected override double StrainValueOf(DifficultyHitObject current)
+ {
+ var maniaCurrent = (ManiaDifficultyHitObject)current;
+ var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime;
+
+ double holdFactor = 1.0; // Factor in case something else is held
+ double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
+
+ for (int i = 0; i < columnCount; i++)
+ {
+ // If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
+ if (current.BaseObject.StartTime < holdEndTimes[i] && endTime > holdEndTimes[i])
+ holdAddition = 1.0;
+
+ // ... this addition only is valid if there is _no_ other note with the same ending.
+ // Releasing multiple notes at the same time is just as easy as releasing one
+ if (endTime == holdEndTimes[i])
+ holdAddition = 0;
+
+ // We give a slight bonus if something is held meanwhile
+ if (holdEndTimes[i] > endTime)
+ holdFactor = 1.25;
+ }
+
+ holdEndTimes[maniaCurrent.BaseObject.Column] = endTime;
+
+ return (1 + holdAddition) * holdFactor;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs
index 9bf14b0672..d64c5dbc6a 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics;
diff --git a/osu.Game.Rulesets.Mania/ManiaInputManager.cs b/osu.Game.Rulesets.Mania/ManiaInputManager.cs
index 864084b407..292990fd7e 100644
--- a/osu.Game.Rulesets.Mania/ManiaInputManager.cs
+++ b/osu.Game.Rulesets.Mania/ManiaInputManager.cs
@@ -19,6 +19,7 @@ namespace osu.Game.Rulesets.Mania
{
[Description("Special 1")]
Special1 = 1,
+
[Description("Special 2")]
Special2,
@@ -26,38 +27,55 @@ namespace osu.Game.Rulesets.Mania
// above at a later time, without breaking replays/configs.
[Description("Key 1")]
Key1 = 10,
+
[Description("Key 2")]
Key2,
+
[Description("Key 3")]
Key3,
+
[Description("Key 4")]
Key4,
+
[Description("Key 5")]
Key5,
+
[Description("Key 6")]
Key6,
+
[Description("Key 7")]
Key7,
+
[Description("Key 8")]
Key8,
+
[Description("Key 9")]
Key9,
+
[Description("Key 10")]
Key10,
+
[Description("Key 11")]
Key11,
+
[Description("Key 12")]
Key12,
+
[Description("Key 13")]
Key13,
+
[Description("Key 14")]
Key14,
+
[Description("Key 15")]
Key15,
+
[Description("Key 16")]
Key16,
+
[Description("Key 17")]
Key17,
+
[Description("Key 18")]
Key18,
}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 57728dd134..b40093844b 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Mania
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
- public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaConfigManager(settings, RulesetInfo);
+ public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaRulesetConfigManager(settings, RulesetInfo);
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
@@ -330,12 +330,12 @@ namespace osu.Game.Rulesets.Mania
for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++)
bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++));
- for (int i = 0; i < columns / 2; i++)
- bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++));
-
if (columns % 2 == 1)
bindings.Add(new KeyBinding(SpecialKey, SpecialAction));
+ for (int i = 0; i < columns / 2; i++)
+ bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++));
+
nextNormalAction = currentNormalAction;
return bindings;
}
@@ -349,6 +349,7 @@ namespace osu.Game.Rulesets.Mania
/// Number of columns in this stage lies at (item - Single).
///
Single = 0,
+
///
/// Columns are grouped into two stages.
/// Overall number of columns lies at (item - Dual), further computation is required for
diff --git a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
index 2ab40b2bc6..2ebfd0cfc1 100644
--- a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
+++ b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
@@ -22,19 +22,19 @@ namespace osu.Game.Rulesets.Mania
[BackgroundDependencyLoader]
private void load()
{
- var config = (ManiaConfigManager)Config;
+ var config = (ManiaRulesetConfigManager)Config;
Children = new Drawable[]
{
new SettingsEnumDropdown
{
LabelText = "Scrolling direction",
- Bindable = config.GetBindable(ManiaSetting.ScrollDirection)
+ Bindable = config.GetBindable(ManiaRulesetSetting.ScrollDirection)
},
new SettingsSlider
{
LabelText = "Scroll speed",
- Bindable = config.GetBindable(ManiaSetting.ScrollTime)
+ Bindable = config.GetBindable(ManiaRulesetSetting.ScrollTime)
},
};
}
diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs
index a88512e12f..a9cd7f2476 100644
--- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs
+++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs
@@ -37,11 +37,11 @@ namespace osu.Game.Rulesets.Mania.MathUtils
/// The random value.
public uint NextUInt()
{
- uint t = X ^ X << 11;
+ uint t = X ^ (X << 11);
X = Y;
Y = Z;
Z = W;
- return W = W ^ W >> 19 ^ t ^ t >> 8;
+ return W = W ^ (W >> 19) ^ t ^ (t >> 8);
}
///
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
index 7b2ee9a632..c78bf72979 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
@@ -1,15 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
-using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
- public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToBeatmap
+ public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter
{
public override string Name => "Dual Stages";
public override string Acronym => "DS";
@@ -29,24 +27,7 @@ namespace osu.Game.Rulesets.Mania.Mods
if (isForCurrentRuleset)
return;
- mbc.TargetColumns *= 2;
- }
-
- public void ApplyToBeatmap(Beatmap beatmap)
- {
- if (isForCurrentRuleset)
- return;
-
- var maniaBeatmap = (ManiaBeatmap)beatmap;
-
- var newDefinitions = new List();
- foreach (var existing in maniaBeatmap.Stages)
- {
- newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
- newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
- }
-
- maniaBeatmap.Stages = newDefinitions;
+ mbc.Dual = true;
}
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs
index baa757008f..6893e1e73b 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects;
@@ -51,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.Mods
}
}
- protected override void OnComboChange(int newCombo)
+ protected override void OnComboChange(ValueChangedEvent e)
{
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 8b2da60a9e..4bfd940aa0 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
@@ -75,16 +76,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
AddNested(Tail);
}
- protected override void OnDirectionChanged(ScrollingDirection direction)
+ protected override void OnDirectionChanged(ValueChangedEvent e)
{
- base.OnDirectionChanged(direction);
+ base.OnDirectionChanged(e);
- bodyPiece.Anchor = bodyPiece.Origin = direction == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
+ bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
}
public override Color4 AccentColour
{
- get { return base.AccentColour; }
+ get => base.AccentColour;
set
{
base.AccentColour = value;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
index eb7d153a90..43aac7907f 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public override Color4 AccentColour
{
- get { return base.AccentColour; }
+ get => base.AccentColour;
set
{
base.AccentColour = value;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
index bc34648dd8..a78524011f 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
@@ -3,7 +3,7 @@
using JetBrains.Annotations;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
@@ -41,9 +41,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override bool ShouldBeAlive => AlwaysAlive || base.ShouldBeAlive;
- protected virtual void OnDirectionChanged(ScrollingDirection direction)
+ protected virtual void OnDirectionChanged(ValueChangedEvent e)
{
- Anchor = Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
+ Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 7ed8e89f95..7ef90cdb9c 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.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.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osuTK.Graphics;
using osu.Framework.Graphics;
@@ -31,16 +32,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
InternalChild = headPiece = new NotePiece();
}
- protected override void OnDirectionChanged(ScrollingDirection direction)
+ protected override void OnDirectionChanged(ValueChangedEvent e)
{
- base.OnDirectionChanged(direction);
+ base.OnDirectionChanged(e);
- headPiece.Anchor = headPiece.Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
+ headPiece.Anchor = headPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
}
public override Color4 AccentColour
{
- get { return base.AccentColour; }
+ get => base.AccentColour;
set
{
base.AccentColour = value;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
index 3decd46888..2baf1ad520 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
@@ -77,11 +77,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
if (accentColour == value)
return;
+
accentColour = value;
updateAccentColour();
@@ -90,7 +91,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
public bool Hitting
{
- get { return hitting; }
+ get => hitting;
set
{
hitting = value;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs
index 4a54ac0aac..b146a33fd3 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs
@@ -35,13 +35,15 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
if (accentColour == value)
return;
+
accentColour = value;
updateGlow();
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs
index 8927e0b068..9e0307c5c2 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/LaneGlowPiece.cs
@@ -78,8 +78,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
public Color4 AccentColour
{
- get { return Colour; }
- set { Colour = value; }
+ get => Colour;
+ set => Colour = value;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
index 65a376a3a8..bb33693783 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -49,20 +49,22 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
private void load(IScrollingInfo scrollingInfo)
{
direction.BindTo(scrollingInfo.Direction);
- direction.BindValueChanged(direction =>
+ direction.BindValueChanged(dir =>
{
- colouredBox.Anchor = colouredBox.Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
+ colouredBox.Anchor = colouredBox.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
}, true);
}
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
if (accentColour == value)
return;
+
accentColour = value;
colouredBox.Colour = AccentColour.Lighten(0.9f);
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
index 8bb22fb586..5e9f46d9c7 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
@@ -17,9 +17,10 @@ namespace osu.Game.Rulesets.Mania.Objects
public double EndTime => StartTime + Duration;
private double duration;
+
public double Duration
{
- get { return duration; }
+ get => duration;
set
{
duration = value;
@@ -29,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Objects
public override double StartTime
{
- get { return base.StartTime; }
+ get => base.StartTime;
set
{
base.StartTime = value;
@@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.Objects
public override int Column
{
- get { return base.Column; }
+ get => base.Column;
set
{
base.Column = value;
diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
index 5765817b63..70720a926b 100644
--- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
@@ -1,7 +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.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Rulesets.Mania.Objects.Types;
using osu.Game.Rulesets.Objects;
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Objects
public virtual int Column
{
- get => ColumnBindable;
+ get => ColumnBindable.Value;
set => ColumnBindable.Value = value;
}
diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs
deleted file mode 100644
index b6ea8c8665..0000000000
--- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs
+++ /dev/null
@@ -1,112 +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.Objects.Types;
-using System;
-
-namespace osu.Game.Rulesets.Mania.Objects
-{
- internal class ManiaHitObjectDifficulty
- {
- ///
- /// Factor by how much individual / overall strain decays per second.
- ///
- ///
- /// These values are results of tweaking a lot and taking into account general feedback.
- ///
- internal const double INDIVIDUAL_DECAY_BASE = 0.125;
- internal const double OVERALL_DECAY_BASE = 0.30;
-
- internal ManiaHitObject BaseHitObject;
-
- private readonly int beatmapColumnCount;
-
- private readonly double endTime;
- private readonly double[] heldUntil;
-
- ///
- /// Measures jacks or more generally: repeated presses of the same button
- ///
- private readonly double[] individualStrains;
-
- internal double IndividualStrain
- {
- get
- {
- return individualStrains[BaseHitObject.Column];
- }
-
- set
- {
- individualStrains[BaseHitObject.Column] = value;
- }
- }
-
- ///
- /// Measures note density in a way
- ///
- internal double OverallStrain = 1;
-
- public ManiaHitObjectDifficulty(ManiaHitObject baseHitObject, int columnCount)
- {
- BaseHitObject = baseHitObject;
-
- endTime = (baseHitObject as IHasEndTime)?.EndTime ?? baseHitObject.StartTime;
-
- beatmapColumnCount = columnCount;
- heldUntil = new double[beatmapColumnCount];
- individualStrains = new double[beatmapColumnCount];
-
- for (int i = 0; i < beatmapColumnCount; ++i)
- {
- individualStrains[i] = 0;
- heldUntil[i] = 0;
- }
- }
-
- internal void CalculateStrains(ManiaHitObjectDifficulty previousHitObject, double timeRate)
- {
- // TODO: Factor in holds
- double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
- double individualDecay = Math.Pow(INDIVIDUAL_DECAY_BASE, timeElapsed / 1000);
- double overallDecay = Math.Pow(OVERALL_DECAY_BASE, timeElapsed / 1000);
-
- double holdFactor = 1.0; // Factor to all additional strains in case something else is held
- double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
-
- // Fill up the heldUntil array
- for (int i = 0; i < beatmapColumnCount; ++i)
- {
- heldUntil[i] = previousHitObject.heldUntil[i];
-
- // If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
- if (BaseHitObject.StartTime < heldUntil[i] && endTime > heldUntil[i])
- {
- holdAddition = 1.0;
- }
-
- // ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1
- if (endTime == heldUntil[i])
- {
- holdAddition = 0;
- }
-
- // We give a slight bonus to everything if something is held meanwhile
- if (heldUntil[i] > endTime)
- {
- holdFactor = 1.25;
- }
-
- // Decay individual strains
- individualStrains[i] = previousHitObject.individualStrains[i] * individualDecay;
- }
-
- heldUntil[BaseHitObject.Column] = endTime;
-
- // Increase individual strain in own column
- IndividualStrain += 2.0 * holdFactor;
-
- OverallStrain = previousHitObject.OverallStrain * overallDecay + (1.0 + holdAddition) * holdFactor;
- }
- }
-}
diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs
index 9ebe638c30..5f2ceab48b 100644
--- a/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs
+++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Objects
private static readonly IReadOnlyDictionary base_ranges = new Dictionary
{
{ HitResult.Perfect, (44.8, 38.8, 27.8) },
- { HitResult.Great, (128, 98, 68 ) },
+ { HitResult.Great, (128, 98, 68) },
{ HitResult.Good, (194, 164, 134) },
{ HitResult.Ok, (254, 224, 194) },
{ HitResult.Meh, (302, 272, 242) },
diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/diffcalc-test.osu
new file mode 100644
index 0000000000..4c877c6193
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/diffcalc-test.osu
@@ -0,0 +1,180 @@
+osu file format v14
+
+[General]
+Mode: 3
+
+[Difficulty]
+CircleSize:4
+OverallDifficulty:7
+ApproachRate:8.3
+SliderMultiplier:1.6
+SliderTickRate:1
+
+[TimingPoints]
+500,500,4,2,1,50,1,0
+37500,-50,4,2,1,50,0,0
+41500,-25,4,2,1,50,0,0
+
+[HitObjects]
+// jacks spaced 1/1 beat apart
+64,192,0,1,0,0:0:0:0:
+64,192,500,1,0,0:0:0:0:
+64,192,1000,1,0,0:0:0:0:
+64,192,1500,1,0,0:0:0:0:
+64,192,2000,1,0,0:0:0:0:
+64,192,2500,1,0,0:0:0:0:
+
+// jacks spaced 1/2 beat apart
+64,192,3500,1,0,0:0:0:0:
+64,192,3750,1,0,0:0:0:0:
+64,192,4000,1,0,0:0:0:0:
+64,192,4250,1,0,0:0:0:0:
+64,192,4500,1,0,0:0:0:0:
+64,192,4750,1,0,0:0:0:0:
+64,192,5000,1,0,0:0:0:0:
+64,192,6000,1,0,0:0:0:0:
+
+// doubles jacks spaced 1/2 beat apart
+192,192,6000,1,0,0:0:0:0:
+64,192,6250,1,0,0:0:0:0:
+192,192,6250,1,0,0:0:0:0:
+64,192,6500,1,0,0:0:0:0:
+192,192,6500,1,0,0:0:0:0:
+64,192,6750,1,0,0:0:0:0:
+192,192,6750,1,0,0:0:0:0:
+64,192,7000,1,0,0:0:0:0:
+192,192,7000,1,0,0:0:0:0:
+64,192,7250,1,0,0:0:0:0:
+192,192,7250,1,0,0:0:0:0:
+64,192,7500,1,0,0:0:0:0:
+192,192,7500,1,0,0:0:0:0:
+
+// trill spaced 1/2 beat apart
+64,192,8500,1,0,0:0:0:0:
+192,192,8750,1,0,0:0:0:0:
+64,192,9000,1,0,0:0:0:0:
+192,192,9250,1,0,0:0:0:0:
+64,192,9500,1,0,0:0:0:0:
+192,192,9750,1,0,0:0:0:0:
+64,192,10000,1,0,0:0:0:0:
+192,192,10250,1,0,0:0:0:0:
+64,192,10500,1,0,0:0:0:0:
+
+// stair spaced 1/4 apart
+64,192,11500,1,0,0:0:0:0:
+192,192,11625,1,0,0:0:0:0:
+320,192,11750,1,0,0:0:0:0:
+448,192,11875,1,0,0:0:0:0:
+320,192,12000,1,0,0:0:0:0:
+192,192,12125,1,0,0:0:0:0:
+64,192,12250,1,0,0:0:0:0:
+192,192,12375,1,0,0:0:0:0:
+320,192,12500,1,0,0:0:0:0:
+448,192,12625,1,0,0:0:0:0:
+
+// jumpstreams?
+64,192,13500,1,0,0:0:0:0:
+192,192,13625,1,0,0:0:0:0:
+320,192,13750,1,0,0:0:0:0:
+448,192,13875,1,0,0:0:0:0:
+320,192,14000,1,0,0:0:0:0:
+192,192,14000,1,0,0:0:0:0:
+64,192,14125,1,0,0:0:0:0:
+192,192,14250,1,0,0:0:0:0:
+320,192,14250,1,0,0:0:0:0:
+448,192,14250,1,0,0:0:0:0:
+64,192,14375,1,0,0:0:0:0:
+64,192,14500,1,0,0:0:0:0:
+320,192,14625,1,0,0:0:0:0:
+448,192,14625,1,0,0:0:0:0:
+192,192,14625,1,0,0:0:0:0:
+192,192,14750,1,0,0:0:0:0:
+64,192,14875,1,0,0:0:0:0:
+192,192,15000,1,0,0:0:0:0:
+320,192,15125,1,0,0:0:0:0:
+448,192,15125,1,0,0:0:0:0:
+
+// double... jumps?
+64,192,16000,1,0,0:0:0:0:
+64,192,16250,1,0,0:0:0:0:
+192,192,16250,1,0,0:0:0:0:
+192,192,16500,1,0,0:0:0:0:
+320,192,16500,1,0,0:0:0:0:
+320,192,16750,1,0,0:0:0:0:
+448,192,16750,1,0,0:0:0:0:
+448,192,17000,1,0,0:0:0:0:
+
+// notes alongside hold
+64,192,18000,128,0,18500:0:0:0:0:
+192,192,18000,1,0,0:0:0:0:
+192,192,18250,1,0,0:0:0:0:
+192,192,18500,1,0,0:0:0:0:
+
+// notes overlapping hold
+64,192,19500,1,0,0:0:0:0:
+192,192,19625,128,0,20875:0:0:0:0:
+64,192,19750,1,0,0:0:0:0:
+64,192,20000,1,0,0:0:0:0:
+64,192,20250,1,0,0:0:0:0:
+64,192,20500,1,0,0:0:0:0:
+64,192,20750,1,0,0:0:0:0:
+64,192,21000,1,0,0:0:0:0:
+
+// simultaneous holds
+64,192,22000,128,0,23000:0:0:0:0:
+192,192,22000,128,0,23000:0:0:0:0:
+320,192,22000,128,0,23000:0:0:0:0:
+448,192,22000,128,0,23000:0:0:0:0:
+
+// hold stairs
+64,192,24500,128,0,25500:0:0:0:0:
+192,192,24625,128,0,25375:0:0:0:0:
+320,192,24750,128,0,25250:0:0:0:0:
+448,192,24875,128,0,25125:0:0:0:0:
+448,192,25375,128,0,26375:0:0:0:0:
+320,192,25500,128,0,26250:0:0:0:0:
+192,192,25625,128,0,26125:0:0:0:0:
+64,192,25750,128,0,26000:0:0:0:0:
+
+// quads
+64,192,26500,1,0,0:0:0:0:
+64,192,27500,1,0,0:0:0:0:
+192,192,27500,1,0,0:0:0:0:
+320,192,27500,1,0,0:0:0:0:
+448,192,27500,1,0,0:0:0:0:
+64,192,27750,1,0,0:0:0:0:
+192,192,27750,1,0,0:0:0:0:
+320,192,27750,1,0,0:0:0:0:
+448,192,27750,1,0,0:0:0:0:
+64,192,28000,1,0,0:0:0:0:
+192,192,28000,1,0,0:0:0:0:
+320,192,28000,1,0,0:0:0:0:
+448,192,28000,1,0,0:0:0:0:
+64,192,28250,1,0,0:0:0:0:
+192,192,28250,1,0,0:0:0:0:
+320,192,28250,1,0,0:0:0:0:
+448,192,28250,1,0,0:0:0:0:
+64,192,28500,1,0,0:0:0:0:
+192,192,28500,1,0,0:0:0:0:
+320,192,28500,1,0,0:0:0:0:
+448,192,28500,1,0,0:0:0:0:
+
+// double-trills
+64,192,29500,1,0,0:0:0:0:
+192,192,29500,1,0,0:0:0:0:
+320,192,29625,1,0,0:0:0:0:
+448,192,29625,1,0,0:0:0:0:
+64,192,29750,1,0,0:0:0:0:
+192,192,29750,1,0,0:0:0:0:
+320,192,29875,1,0,0:0:0:0:
+448,192,29875,1,0,0:0:0:0:
+64,192,30000,1,0,0:0:0:0:
+192,192,30000,1,0,0:0:0:0:
+320,192,30125,1,0,0:0:0:0:
+448,192,30125,1,0,0:0:0:0:
+64,192,30250,1,0,0:0:0:0:
+192,192,30250,1,0,0:0:0:0:
+320,192,30375,1,0,0:0:0:0:
+448,192,30375,1,0,0:0:0:0:
+64,192,30500,1,0,0:0:0:0:
+192,192,30500,1,0,0:0:0:0:
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index b94b64fa36..cf3d0734fb 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -159,6 +159,6 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
}
- protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
+ public override HitWindows CreateHitWindows() => new ManiaHitWindows();
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 3e7884af6c..c59bed4ea7 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.UI.Components;
@@ -82,28 +82,30 @@ namespace osu.Game.Rulesets.Mania.UI
TopLevelContainer.Add(explosionContainer.CreateProxy());
- Direction.BindValueChanged(d =>
+ Direction.BindValueChanged(dir =>
{
hitTargetContainer.Padding = new MarginPadding
{
- Top = d == ScrollingDirection.Up ? ManiaStage.HIT_TARGET_POSITION : 0,
- Bottom = d == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0,
+ Top = dir.NewValue == ScrollingDirection.Up ? ManiaStage.HIT_TARGET_POSITION : 0,
+ Bottom = dir.NewValue == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0,
};
- keyArea.Anchor = keyArea.Origin= d == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
+ keyArea.Anchor = keyArea.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
}, true);
}
public override Axes RelativeSizeAxes => Axes.Y;
private bool isSpecial;
+
public bool IsSpecial
{
- get { return isSpecial; }
+ get => isSpecial;
set
{
if (isSpecial == value)
return;
+
isSpecial = value;
Width = isSpecial ? special_column_width : column_width;
@@ -111,13 +113,15 @@ namespace osu.Game.Rulesets.Mania.UI
}
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
if (accentColour == value)
return;
+
accentColour = value;
background.AccentColour = value;
@@ -156,7 +160,7 @@ namespace osu.Game.Rulesets.Mania.UI
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements)
+ if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value)
return;
explosionContainer.Add(new HitExplosion(judgedObject)
@@ -167,7 +171,7 @@ namespace osu.Game.Rulesets.Mania.UI
public bool OnPressed(ManiaAction action)
{
- if (action != Action)
+ if (action != Action.Value)
return false;
var nextObject =
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
index b7f291b5a2..b4e29ae9f9 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@@ -48,9 +48,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components
};
direction.BindTo(scrollingInfo.Direction);
- direction.BindValueChanged(direction =>
+ direction.BindValueChanged(dir =>
{
- backgroundOverlay.Anchor = backgroundOverlay.Origin = direction == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
+ backgroundOverlay.Anchor = backgroundOverlay.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
updateColours();
}, true);
}
@@ -70,6 +70,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
if (accentColour == value)
return;
+
accentColour = value;
updateColours();
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
index 7f5687e600..89e8cd9b5a 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -49,9 +49,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components
private void load(IScrollingInfo scrollingInfo)
{
direction.BindTo(scrollingInfo.Direction);
- direction.BindValueChanged(direction =>
+ direction.BindValueChanged(dir =>
{
- Anchor anchor = direction == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
+ Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
hitTargetBar.Anchor = hitTargetBar.Origin = anchor;
hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
@@ -73,6 +73,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
if (accentColour == value)
return;
+
accentColour = value;
updateColours();
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
index a180ffbd3b..03b55cbead 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@@ -64,11 +64,11 @@ namespace osu.Game.Rulesets.Mania.UI.Components
};
direction.BindTo(scrollingInfo.Direction);
- direction.BindValueChanged(direction =>
+ direction.BindValueChanged(dir =>
{
gradient.Colour = ColourInfo.GradientVertical(
- direction == ScrollingDirection.Up ? Color4.Black : Color4.Black.Opacity(0),
- direction == ScrollingDirection.Up ? Color4.Black.Opacity(0) : Color4.Black);
+ dir.NewValue == ScrollingDirection.Up ? Color4.Black : Color4.Black.Opacity(0),
+ dir.NewValue == ScrollingDirection.Up ? Color4.Black.Opacity(0) : Color4.Black);
}, true);
}
@@ -87,6 +87,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
if (accentColour == value)
return;
+
accentColour = value;
updateColours();
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
index 1469924bb2..5874bac7f6 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.UI
private void load()
{
if (JudgementText != null)
- JudgementText.TextSize = 25;
+ JudgementText.Font = JudgementText.Font.With(size: 25);
}
protected override void LoadComplete()
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
index 892ad584dc..d8b7dc0381 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
@@ -4,7 +4,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
@@ -15,7 +15,6 @@ using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
-using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
@@ -35,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.UI
public IEnumerable BarLines;
- protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
+ protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
private readonly Bindable configDirection = new Bindable();
@@ -75,10 +74,10 @@ namespace osu.Game.Rulesets.Mania.UI
{
BarLines.ForEach(Playfield.Add);
- Config.BindWith(ManiaSetting.ScrollDirection, configDirection);
- configDirection.BindValueChanged(v => Direction.Value = (ScrollingDirection)v, true);
+ Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
+ configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
- Config.BindWith(ManiaSetting.ScrollTime, TimeRange);
+ Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange);
}
///
@@ -96,7 +95,7 @@ namespace osu.Game.Rulesets.Mania.UI
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
- public override int Variant => (int)(Mods.OfType().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single) + Beatmap.TotalColumns;
+ public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
index bce333ff34..a28de7ea58 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
@@ -136,12 +136,12 @@ namespace osu.Game.Rulesets.Mania.UI
AddColumn(column);
}
- Direction.BindValueChanged(d =>
+ Direction.BindValueChanged(dir =>
{
barLineContainer.Padding = new MarginPadding
{
- Top = d == ScrollingDirection.Up ? HIT_TARGET_POSITION : 0,
- Bottom = d == ScrollingDirection.Down ? HIT_TARGET_POSITION : 0,
+ Top = dir.NewValue == ScrollingDirection.Up ? HIT_TARGET_POSITION : 0,
+ Bottom = dir.NewValue == ScrollingDirection.Down ? HIT_TARGET_POSITION : 0,
};
}, true);
}
@@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Mania.UI
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!judgedObject.DisplayResult || !DisplayJudgements)
+ if (!judgedObject.DisplayResult || !DisplayJudgements.Value)
return;
judgements.Clear();
diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj
index 349e46e02d..9930a166e3 100644
--- a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj
@@ -39,10 +39,6 @@
{C92A607B-1FDD-4954-9F92-03FF547D9080}
osu.Game.Rulesets.Osu
-
- {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}
- osu.Game.Resources
-
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
index 0ebcad3637..f7d1ff4db1 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
@@ -33,9 +33,11 @@ namespace osu.Game.Rulesets.Osu.Tests
case Slider slider:
foreach (var nested in slider.NestedHitObjects)
yield return createConvertValue(nested);
+
break;
default:
yield return createConvertValue(hitObject);
+
break;
}
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
new file mode 100644
index 0000000000..e55dc1f902
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
@@ -0,0 +1,25 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Osu.Difficulty;
+using osu.Game.Tests.Beatmaps;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [TestFixture]
+ public class OsuDifficultyCalculatorTest : DifficultyCalculatorTest
+ {
+ protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
+
+ [TestCase(6.931145117263422, "diffcalc-test")]
+ public void Test(double expected, string name)
+ => base.Test(expected, name);
+
+ protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap);
+
+ protected override Ruleset CreateRuleset() => new OsuRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs
index 6ebfe4fad1..3d553c334f 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseGameplayCursor.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
private GameplayCursor cursor;
- public override IReadOnlyList RequiredTypes => new [] { typeof(CursorTrail) };
+ public override IReadOnlyList RequiredTypes => new[] { typeof(CursorTrail) };
public CursorContainer Cursor => cursor;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs
index 5484f15581..e1e854e8dc 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs
@@ -89,7 +89,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
private readonly bool auto;
- public TestDrawableHitCircle(HitCircle h, bool auto) : base(h)
+ public TestDrawableHitCircle(HitCircle h, bool auto)
+ : base(h)
{
this.auto = auto;
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleLongCombo.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleLongCombo.cs
new file mode 100644
index 0000000000..f5fe36b56a
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleLongCombo.cs
@@ -0,0 +1,36 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Osu.Objects;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [TestFixture]
+ public class TestCaseHitCircleLongCombo : Game.Tests.Visual.TestCasePlayer
+ {
+ public TestCaseHitCircleLongCombo()
+ : base(new OsuRuleset())
+ {
+ }
+
+ protected override IBeatmap CreateBeatmap(Ruleset ruleset)
+ {
+ var beatmap = new Beatmap
+ {
+ BeatmapInfo = new BeatmapInfo
+ {
+ BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
+ Ruleset = ruleset.RulesetInfo
+ }
+ };
+
+ for (int i = 0; i < 512; i++)
+ beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 });
+
+ return beatmap;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
index d6b88c1c5f..35e8f3e17e 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
@@ -16,6 +16,7 @@ using osuTK.Graphics;
using osu.Game.Rulesets.Mods;
using System.Linq;
using NUnit.Framework;
+using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
@@ -300,6 +301,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}
private float judgementOffsetDirection = 1;
+
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
var osuObject = judgedObject as DrawableOsuHitObject;
@@ -313,7 +315,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Origin = Anchor.Centre,
Text = result.IsHit ? "Hit!" : "Miss!",
Colour = result.IsHit ? Color4.Green : Color4.Red,
- TextSize = 30,
+ Font = OsuFont.GetFont(size: 30),
Position = osuObject.HitObject.StackedEndPosition + judgementOffsetDirection * new Vector2(0, 45)
});
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderInput.cs
new file mode 100644
index 0000000000..57effe01f1
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderInput.cs
@@ -0,0 +1,372 @@
+// 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.Screens;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Replays;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Replays;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
+using osu.Game.Screens.Play;
+using osu.Game.Tests.Beatmaps;
+using osu.Game.Tests.Visual;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestCaseSliderInput : RateAdjustedBeatmapTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(SliderBall),
+ typeof(DrawableSlider),
+ typeof(DrawableSliderTick),
+ typeof(DrawableRepeatPoint),
+ typeof(DrawableOsuHitObject),
+ typeof(DrawableSliderHead),
+ typeof(DrawableSliderTail),
+ };
+
+ private const double time_before_slider = 250;
+ private const double time_slider_start = 1500;
+ private const double time_during_slide_1 = 2500;
+ private const double time_during_slide_2 = 3000;
+ private const double time_during_slide_3 = 3500;
+ private const double time_during_slide_4 = 3800;
+
+ private List judgementResults;
+ private bool allJudgedFired;
+
+ ///
+ /// Scenario:
+ /// - Press a key before a slider starts
+ /// - Press the other key on the slider head timed correctly while holding the original key
+ /// - Release the latter pressed key
+ /// Expected Result:
+ /// A passing test case will have the cursor lose tracking on replay frame 3.
+ ///
+ [Test]
+ public void TestInvalidKeyTransfer()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton, OsuAction.RightButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_1 },
+ });
+
+ AddAssert("Tracking lost", assertMidSliderJudgementFail);
+ }
+
+ ///
+ /// Scenario:
+ /// - Press a key on the slider head timed correctly
+ /// - Press the other key in the middle of the slider while holding the original key
+ /// - Release the original key used to hit the slider
+ /// Expected Result:
+ /// A passing test case will have the cursor continue tracking on replay frame 3.
+ ///
+ [Test]
+ public void TestLeftBeforeSliderThenRightThenLettingGoOfLeft()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton, OsuAction.RightButton }, Time = time_during_slide_1 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_2 },
+ });
+
+ AddAssert("Tracking retained", assertGreatJudge);
+ }
+
+ ///
+ /// Scenario:
+ /// - Press a key on the slider head timed correctly
+ /// - Press the other key in the middle of the slider while holding the original key
+ /// - Release the new key that was pressed second
+ /// Expected Result:
+ /// A passing test case will have the cursor continue tracking on replay frame 3.
+ ///
+ [Test]
+ public void TestTrackingRetentionLeftRightLeft()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton, OsuAction.RightButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_1 },
+ });
+
+ AddAssert("Tracking retained", assertGreatJudge);
+ }
+
+ ///
+ /// Scenario:
+ /// - Press a key before a slider starts
+ /// - Press the other key on the slider head timed correctly while holding the original key
+ /// - Release the key that was held down before the slider started.
+ /// Expected Result:
+ /// A passing test case will have the cursor continue tracking on replay frame 3
+ ///
+ [Test]
+ public void TestTrackingLeftBeforeSliderToRight()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton, OsuAction.RightButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_1 },
+ });
+
+ AddAssert("Tracking retained", assertGreatJudge);
+ }
+
+ ///
+ /// Scenario:
+ /// - Press a key before a slider starts
+ /// - Hold the key down throughout the slider without pressing any other buttons.
+ /// Expected Result:
+ /// A passing test case will have the cursor track the slider, but miss the slider head.
+ ///
+ [Test]
+ public void TestTrackingPreclicked()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
+ });
+
+ AddAssert("Tracking retained, sliderhead miss", assertHeadMissTailTracked);
+ }
+
+ ///
+ /// Scenario:
+ /// - Press a key before a slider starts
+ /// - Hold the key down after the slider starts
+ /// - Move the cursor away from the slider body
+ /// - Move the cursor back onto the body
+ /// Expected Result:
+ /// A passing test case will have the cursor track the slider, miss the head, miss the ticks where its outside of the body, and resume tracking when the cursor returns.
+ ///
+ [Test]
+ public void TestTrackingReturnMidSlider()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(150, 150), Actions = { OsuAction.LeftButton }, Time = time_during_slide_1 },
+ new OsuReplayFrame { Position = new Vector2(200, 200), Actions = { OsuAction.LeftButton }, Time = time_during_slide_2 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_3 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
+ });
+
+ AddAssert("Tracking re-acquired", assertMidSliderJudgements);
+ }
+
+ ///
+ /// Scenario:
+ /// - Press a key before a slider starts
+ /// - Press the other key on the slider head timed correctly while holding the original key
+ /// - Release the key used to hit the slider head
+ /// - While holding the first key, move the cursor away from the slider body
+ /// - Still holding the first key, move the cursor back to the slider body
+ /// Expected Result:
+ /// A passing test case will have the slider not track despite having the cursor return to the slider body.
+ ///
+ [Test]
+ public void TestTrackingReturnMidSliderKeyDownBefore()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton, OsuAction.RightButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_1 },
+ new OsuReplayFrame { Position = new Vector2(200, 200), Actions = { OsuAction.LeftButton }, Time = time_during_slide_2 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_3 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
+ });
+
+ AddAssert("Tracking lost", assertMidSliderJudgementFail);
+ }
+
+ ///
+ /// Scenario:
+ /// - Wait for the slider to reach a mid-point
+ /// - Press a key away from the slider body
+ /// - While holding down the key, move into the slider body
+ /// Expected Result:
+ /// A passing test case will have the slider track the cursor after the cursor enters the slider body.
+ ///
+ [Test]
+ public void TestTrackingMidSlider()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(150, 150), Actions = { OsuAction.LeftButton }, Time = time_during_slide_1 },
+ new OsuReplayFrame { Position = new Vector2(200, 200), Actions = { OsuAction.LeftButton }, Time = time_during_slide_2 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_3 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
+ });
+
+ AddAssert("Tracking acquired", assertMidSliderJudgements);
+ }
+
+ ///
+ /// Scenario:
+ /// - Press a key before the slider starts
+ /// - Press another key on the slider head while holding the original key
+ /// - Move out of the slider body while releasing the two pressed keys
+ /// - Move back into the slider body while pressing any key.
+ /// Expected Result:
+ /// A passing test case will have the slider track the cursor after the cursor enters the slider body.
+ ///
+ [Test]
+ public void TestMidSliderTrackingAcquired()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton, OsuAction.RightButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(100, 100), Time = time_during_slide_1 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_2 },
+ });
+
+ AddAssert("Tracking acquired", assertMidSliderJudgements);
+ }
+
+ [Test]
+ public void TestMidSliderTrackingAcquiredWithMouseDownOutsideSlider()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton, OsuAction.RightButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(100, 100), Actions = { OsuAction.RightButton }, Time = time_during_slide_1 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_2 },
+ });
+
+ AddAssert("Tracking acquired", assertMidSliderJudgements);
+ }
+
+ ///
+ /// Scenario:
+ /// - Press a key on the slider head
+ /// - While holding the key, move outside of the slider body with the cursor
+ /// - Release the key while outside of the slider body
+ /// - Press the key again while outside of the slider body
+ /// - Move back into the slider body while holding the pressed key
+ /// Expected Result:
+ /// A passing test case will have the slider track the cursor after the cursor enters the slider body.
+ ///
+ [Test]
+ public void TestTrackingReleasedValidKey()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(100, 100), Actions = { OsuAction.LeftButton }, Time = time_during_slide_1 },
+ new OsuReplayFrame { Position = new Vector2(100, 100), Time = time_during_slide_2 },
+ new OsuReplayFrame { Position = new Vector2(100, 100), Actions = { OsuAction.LeftButton }, Time = time_during_slide_3 },
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
+ });
+
+ AddAssert("Tracking acquired", assertMidSliderJudgements);
+ }
+
+ private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
+
+ private bool assertHeadMissTailTracked() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
+
+ private bool assertMidSliderJudgements() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great;
+
+ private bool assertMidSliderJudgementFail() => judgementResults[judgementResults.Count - 2].Type == HitResult.Miss;
+
+ private ScoreAccessibleReplayPlayer currentPlayer;
+
+ private void performTest(List frames)
+ {
+ // Empty frame to be added as a workaround for first frame behavior.
+ // If an input exists on the first frame, the input would apply to the entire intro lead-in
+ // Likely requires some discussion regarding how first frame inputs should be handled.
+ frames.Insert(0, new OsuReplayFrame());
+
+ AddStep("load player", () =>
+ {
+ Beatmap.Value = new TestWorkingBeatmap(new Beatmap
+ {
+ HitObjects =
+ {
+ new Slider
+ {
+ StartTime = time_slider_start,
+ Position = new Vector2(0, 0),
+ Path = new SliderPath(PathType.PerfectCurve, new[]
+ {
+ Vector2.Zero,
+ new Vector2(25, 0),
+ }, 25),
+ }
+ },
+ ControlPointInfo =
+ {
+ DifficultyPoints = { new DifficultyControlPoint { SpeedMultiplier = 0.1f } }
+ },
+ BeatmapInfo =
+ {
+ BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 },
+ Ruleset = new OsuRuleset().RulesetInfo
+ },
+ }, Clock);
+
+ var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } })
+ {
+ AllowPause = false,
+ AllowLeadIn = false,
+ AllowResults = false
+ };
+
+ p.OnLoadComplete += _ =>
+ {
+ p.ScoreProcessor.NewJudgement += result =>
+ {
+ if (currentPlayer == p) judgementResults.Add(result);
+ };
+ p.ScoreProcessor.AllJudged += () =>
+ {
+ if (currentPlayer == p) allJudgedFired = true;
+ };
+ };
+
+ LoadScreen(currentPlayer = p);
+ allJudgedFired = false;
+ judgementResults = new List();
+ });
+
+ AddUntilStep(() => Beatmap.Value.Track.CurrentTime == 0, "Beatmap at 0");
+ AddUntilStep(() => currentPlayer.IsCurrentScreen(), "Wait until player is loaded");
+ AddUntilStep(() => allJudgedFired, "Wait for all judged");
+ }
+
+ private class ScoreAccessibleReplayPlayer : ReplayPlayer
+ {
+ public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
+
+ public ScoreAccessibleReplayPlayer(Score score)
+ : base(score)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs
index df903efe3d..e8b534bba9 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public class TestCaseSpinner : OsuTestCase
{
public override IReadOnlyList RequiredTypes => new[]
-{
+ {
typeof(SpinnerDisc),
typeof(DrawableSpinner),
typeof(DrawableOsuHitObject)
@@ -67,7 +67,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
private bool auto;
- public TestDrawableSpinner(Spinner s, bool auto) : base(s)
+ public TestDrawableSpinner(Spinner s, bool auto)
+ : base(s)
{
this.auto = auto;
}
diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs
new file mode 100644
index 0000000000..f6edd062e9
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs
@@ -0,0 +1,30 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Configuration;
+
+namespace osu.Game.Rulesets.Osu.Configuration
+{
+ public class OsuRulesetConfigManager : RulesetConfigManager
+ {
+ public OsuRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
+ : base(settings, ruleset, variant)
+ {
+ }
+
+ protected override void InitialiseDefaults()
+ {
+ base.InitialiseDefaults();
+
+ Set(OsuRulesetSetting.SnakingInSliders, true);
+ Set(OsuRulesetSetting.SnakingOutSliders, true);
+ }
+ }
+
+ public enum OsuRulesetSetting
+ {
+ SnakingInSliders,
+ SnakingOutSliders
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index fd54dc0260..6e991a1d08 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Difficulty;
-using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Difficulty
{
@@ -13,10 +12,5 @@ namespace osu.Game.Rulesets.Osu.Difficulty
public double ApproachRate;
public double OverallDifficulty;
public int MaxCombo;
-
- public OsuDifficultyAttributes(Mod[] mods, double starRating)
- : base(mods, starRating)
- {
- }
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 3d0a1dc266..e2a1542574 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -1,10 +1,13 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Difficulty.Skills;
@@ -15,7 +18,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
public class OsuDifficultyCalculator : DifficultyCalculator
{
- private const int section_length = 400;
private const double difficulty_multiplier = 0.0675;
public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
@@ -23,58 +25,27 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
}
- protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
+ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
- if (!beatmap.HitObjects.Any())
- return new OsuDifficultyAttributes(mods, 0);
-
- OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast().ToList(), timeRate);
- Skill[] skills =
- {
- new Aim(),
- new Speed()
- };
-
- double sectionLength = section_length * timeRate;
-
- // The first object doesn't generate a strain, so we begin with an incremented section end
- double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength;
-
- foreach (OsuDifficultyHitObject h in difficultyBeatmap)
- {
- while (h.BaseObject.StartTime > currentSectionEnd)
- {
- foreach (Skill s in skills)
- {
- s.SaveCurrentPeak();
- s.StartNewSectionFrom(currentSectionEnd);
- }
-
- currentSectionEnd += sectionLength;
- }
-
- foreach (Skill s in skills)
- s.Process(h);
- }
-
- // The peak strain will not be saved for the last section in the above loop
- foreach (Skill s in skills)
- s.SaveCurrentPeak();
+ if (beatmap.HitObjects.Count == 0)
+ return new OsuDifficultyAttributes { Mods = mods };
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
- double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
- double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
+ double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate;
+ double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
int maxCombo = beatmap.HitObjects.Count;
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1);
- return new OsuDifficultyAttributes(mods, starRating)
+ return new OsuDifficultyAttributes
{
+ StarRating = starRating,
+ Mods = mods,
AimStrain = aimRating,
SpeedStrain = speedRating,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
@@ -83,6 +54,26 @@ namespace osu.Game.Rulesets.Osu.Difficulty
};
}
+ protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
+ {
+ // The first jump is formed by the first two hitobjects of the map.
+ // If the map has less than two OsuHitObjects, the enumerator will not return anything.
+ for (int i = 1; i < beatmap.HitObjects.Count; i++)
+ {
+ var lastLast = i > 1 ? beatmap.HitObjects[i - 2] : null;
+ var last = beatmap.HitObjects[i - 1];
+ var current = beatmap.HitObjects[i];
+
+ yield return new OsuDifficultyHitObject(current, lastLast, last, clockRate);
+ }
+ }
+
+ protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
+ {
+ new Aim(),
+ new Speed()
+ };
+
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
{
new OsuModDoubleTime(),
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 83deac78a1..0dce5208dd 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -88,11 +88,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private double computeAimValue()
{
- double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes.AimStrain / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
+ double rawAim = Attributes.AimStrain;
+
+ if (mods.Any(m => m is OsuModTouchDevice))
+ rawAim = Math.Pow(rawAim, 0.8);
+
+ double aimValue = Math.Pow(5.0f * Math.Max(1.0f, rawAim / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
// Longer maps are worth more
double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
- (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
+ (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
aimValue *= lengthBonus;
@@ -108,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
else if (Attributes.ApproachRate < 8.0f)
{
- approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
+ approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
}
aimValue *= approachRateFactor;
@@ -121,8 +126,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
// Apply object-based bonus for flashlight.
aimValue *= 1.0f + 0.35f * Math.Min(1.0f, totalHits / 200.0f) +
- (totalHits > 200 ? 0.3f * Math.Min(1.0f, (totalHits - 200) / 300.0f) +
- (totalHits > 500 ? (totalHits - 500) / 1200.0f : 0.0f) : 0.0f);
+ (totalHits > 200
+ ? 0.3f * Math.Min(1.0f, (totalHits - 200) / 300.0f) +
+ (totalHits > 500 ? (totalHits - 500) / 1200.0f : 0.0f)
+ : 0.0f);
}
// Scale the aim value with accuracy _slightly_
@@ -139,7 +146,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Longer maps are worth more
speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
- (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
+ (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
speedValue *= Math.Pow(0.97f, countMiss);
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs
deleted file mode 100644
index 1736912e1f..0000000000
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using osu.Game.Rulesets.Osu.Objects;
-
-namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
-{
- ///
- /// An enumerable container wrapping input as
- /// which contains extra data required for difficulty calculation.
- ///
- public class OsuDifficultyBeatmap : IEnumerable
- {
- private readonly IEnumerator difficultyObjects;
-
- ///
- /// Creates an enumerator, which preprocesses a list of s recieved as input, wrapping them as
- /// which contains extra data required for difficulty calculation.
- ///
- public OsuDifficultyBeatmap(List objects, double timeRate)
- {
- // Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
- // This should probably happen before the objects reach the difficulty calculator.
- difficultyObjects = createDifficultyObjectEnumerator(objects.OrderBy(h => h.StartTime).ToList(), timeRate);
- }
-
- ///
- /// Returns an enumerator that enumerates all s in the .
- ///
- public IEnumerator GetEnumerator() => difficultyObjects;
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- private IEnumerator createDifficultyObjectEnumerator(List objects, double timeRate)
- {
- // The first jump is formed by the first two hitobjects of the map.
- // If the map has less than two OsuHitObjects, the enumerator will not return anything.
- for (int i = 1; i < objects.Count; i++)
- yield return new OsuDifficultyHitObject(objects[i], objects[i - 1], timeRate);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index 91d6c3d7a3..37276a3432 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -1,24 +1,20 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
{
- ///
- /// A wrapper around extending it with additional data required for difficulty calculation.
- ///
- public class OsuDifficultyHitObject
+ public class OsuDifficultyHitObject : DifficultyHitObject
{
private const int normalized_radius = 52;
- ///
- /// The this refers to.
- ///
- public OsuHitObject BaseObject { get; }
+ protected new OsuHitObject BaseObject => (OsuHitObject)base.BaseObject;
///
/// Normalized distance from the end position of the previous to the start position of this .
@@ -31,31 +27,29 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
public double TravelDistance { get; private set; }
///
- /// Milliseconds elapsed since the StartTime of the previous .
+ /// Angle the player has to take to hit this .
+ /// Calculated as the angle between the circles (current-2, current-1, current).
///
- public double DeltaTime { get; private set; }
+ public double? Angle { get; private set; }
///
/// Milliseconds elapsed since the start time of the previous , with a minimum of 50ms.
///
- public double StrainTime { get; private set; }
+ public readonly double StrainTime;
+ private readonly OsuHitObject lastLastObject;
private readonly OsuHitObject lastObject;
- private readonly double timeRate;
- ///
- /// Initializes the object calculating extra data required for difficulty calculation.
- ///
- public OsuDifficultyHitObject(OsuHitObject currentObject, OsuHitObject lastObject, double timeRate)
+ public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double clockRate)
+ : base(hitObject, lastObject, clockRate)
{
- this.lastObject = lastObject;
- this.timeRate = timeRate;
-
- BaseObject = currentObject;
+ this.lastLastObject = (OsuHitObject)lastLastObject;
+ this.lastObject = (OsuHitObject)lastObject;
setDistances();
- setTimingValues();
- // Calculate angle here
+
+ // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
+ StrainTime = Math.Max(50, DeltaTime);
}
private void setDistances()
@@ -68,34 +62,37 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
scalingFactor *= 1 + smallCircleBonus;
}
- Vector2 lastCursorPosition = lastObject.StackedPosition;
-
- var lastSlider = lastObject as Slider;
- if (lastSlider != null)
+ if (lastObject is Slider lastSlider)
{
computeSliderCursorPosition(lastSlider);
- lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition;
-
TravelDistance = lastSlider.LazyTravelDistance * scalingFactor;
}
+ Vector2 lastCursorPosition = getEndCursorPosition(lastObject);
+
// Don't need to jump to reach spinners
if (!(BaseObject is Spinner))
JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
- }
- private void setTimingValues()
- {
- DeltaTime = (BaseObject.StartTime - lastObject.StartTime) / timeRate;
+ if (lastLastObject != null)
+ {
+ Vector2 lastLastCursorPosition = getEndCursorPosition(lastLastObject);
- // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
- StrainTime = Math.Max(50, DeltaTime);
+ Vector2 v1 = lastLastCursorPosition - lastObject.StackedPosition;
+ Vector2 v2 = BaseObject.StackedPosition - lastCursorPosition;
+
+ float dot = Vector2.Dot(v1, v2);
+ float det = v1.X * v2.Y - v1.Y * v2.X;
+
+ Angle = Math.Abs(Math.Atan2(det, dot));
+ }
}
private void computeSliderCursorPosition(Slider slider)
{
if (slider.LazyEndPosition != null)
return;
+
slider.LazyEndPosition = slider.StackedPosition;
float approxFollowCircleRadius = (float)(slider.Radius * 3);
@@ -125,7 +122,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
var scoringTimes = slider.NestedHitObjects.Skip(1).Select(t => t.StartTime);
foreach (var time in scoringTimes)
computeVertex(time);
- computeVertex(slider.EndTime);
+ }
+
+ private Vector2 getEndCursorPosition(OsuHitObject hitObject)
+ {
+ Vector2 pos = hitObject.StackedPosition;
+
+ if (hitObject is Slider slider)
+ {
+ computeSliderCursorPosition(slider);
+ pos = slider.LazyEndPosition ?? pos;
+ }
+
+ return pos;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
index 9e5f13de62..e74f4933b2 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
@@ -2,7 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{
@@ -11,10 +14,46 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public class Aim : Skill
{
+ private const double angle_bonus_begin = Math.PI / 3;
+ private const double timing_threshold = 107;
+
protected override double SkillMultiplier => 26.25;
protected override double StrainDecayBase => 0.15;
- protected override double StrainValueOf(OsuDifficultyHitObject current)
- => (Math.Pow(current.TravelDistance, 0.99) + Math.Pow(current.JumpDistance, 0.99)) / current.StrainTime;
+ protected override double StrainValueOf(DifficultyHitObject current)
+ {
+ if (current.BaseObject is Spinner)
+ return 0;
+
+ var osuCurrent = (OsuDifficultyHitObject)current;
+
+ double result = 0;
+
+ if (Previous.Count > 0)
+ {
+ var osuPrevious = (OsuDifficultyHitObject)Previous[0];
+
+ if (osuCurrent.Angle != null && osuCurrent.Angle.Value > angle_bonus_begin)
+ {
+ const double scale = 90;
+
+ var angleBonus = Math.Sqrt(
+ Math.Max(osuPrevious.JumpDistance - scale, 0)
+ * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2)
+ * Math.Max(osuCurrent.JumpDistance - scale, 0));
+ result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime);
+ }
+ }
+
+ double jumpDistanceExp = applyDiminishingExp(osuCurrent.JumpDistance);
+ double travelDistanceExp = applyDiminishingExp(osuCurrent.TravelDistance);
+
+ return Math.Max(
+ result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold),
+ (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.StrainTime
+ );
+ }
+
+ private double applyDiminishingExp(double val) => Math.Pow(val, 0.99);
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
index 809632806b..46a81a9480 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
@@ -1,7 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{
@@ -10,30 +14,48 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
///
public class Speed : Skill
{
+ private const double single_spacing_threshold = 125;
+
+ private const double angle_bonus_begin = 5 * Math.PI / 6;
+ private const double pi_over_4 = Math.PI / 4;
+ private const double pi_over_2 = Math.PI / 2;
+
protected override double SkillMultiplier => 1400;
protected override double StrainDecayBase => 0.3;
- private const double single_spacing_threshold = 125;
- private const double stream_spacing_threshold = 110;
- private const double almost_diameter = 90;
+ private const double min_speed_bonus = 75; // ~200BPM
+ private const double max_speed_bonus = 45; // ~330BPM
+ private const double speed_balancing_factor = 40;
- protected override double StrainValueOf(OsuDifficultyHitObject current)
+ protected override double StrainValueOf(DifficultyHitObject current)
{
- double distance = current.TravelDistance + current.JumpDistance;
+ if (current.BaseObject is Spinner)
+ return 0;
- double speedValue;
- if (distance > single_spacing_threshold)
- speedValue = 2.5;
- else if (distance > stream_spacing_threshold)
- speedValue = 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold);
- else if (distance > almost_diameter)
- speedValue = 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter);
- else if (distance > almost_diameter / 2)
- speedValue = 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2);
- else
- speedValue = 0.95;
+ var osuCurrent = (OsuDifficultyHitObject)current;
- return speedValue / current.StrainTime;
+ double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance);
+ double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime);
+
+ double speedBonus = 1.0;
+ if (deltaTime < min_speed_bonus)
+ speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2);
+
+ double angleBonus = 1.0;
+ if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin)
+ {
+ angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57;
+ if (osuCurrent.Angle.Value < pi_over_2)
+ {
+ angleBonus = 1.28;
+ if (distance < 90 && osuCurrent.Angle.Value < pi_over_4)
+ angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1);
+ else if (distance < 90)
+ angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - osuCurrent.Angle.Value) / pi_over_4);
+ }
+ }
+
+ return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
index 5958b32f33..7f6a60c400 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
PositionBindable.BindValueChanged(_ => UpdatePosition(), true);
StackHeightBindable.BindValueChanged(_ => UpdatePosition());
- ScaleBindable.BindValueChanged(v => Scale = new Vector2(v), true);
+ ScaleBindable.BindValueChanged(scale => Scale = new Vector2(scale.NewValue), true);
}
protected virtual void UpdatePosition() => Position = hitCircle.StackedPosition;
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitObjectPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitObjectPiece.cs
index 44e61f30b0..315a5a2b9d 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitObjectPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitObjectPiece.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/SliderPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/SliderPiece.cs
index dfc8bf4664..8fd1d6d6f9 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/SliderPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/SliderPiece.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
index cb9b5211d4..179fa1bc72 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
body.BorderColour = colours.Yellow;
PositionBindable.BindValueChanged(_ => updatePosition(), true);
- ScaleBindable.BindValueChanged(v => body.PathWidth = v * 64, true);
+ ScaleBindable.BindValueChanged(scale => body.PathWidth = scale.NewValue * 64, true);
}
private void updatePosition() => Position = slider.StackedPosition;
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs
index d2fec859b8..9d164ebe0b 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
using osu.Game.Rulesets.Osu.Objects;
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs
index 7f91bc49eb..ae94848c81 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components
PositionBindable.BindValueChanged(_ => updatePosition(), true);
StackHeightBindable.BindValueChanged(_ => updatePosition());
- ScaleBindable.BindValueChanged(v => ring.Scale = new Vector2(v), true);
+ ScaleBindable.BindValueChanged(scale => ring.Scale = new Vector2(scale.NewValue), true);
}
private void updatePosition() => Position = spinner.Position;
diff --git a/osu.Game.Rulesets.Osu/Judgements/ComboResult.cs b/osu.Game.Rulesets.Osu/Judgements/ComboResult.cs
index d7c76fccbe..ad292b0439 100644
--- a/osu.Game.Rulesets.Osu/Judgements/ComboResult.cs
+++ b/osu.Game.Rulesets.Osu/Judgements/ComboResult.cs
@@ -9,8 +9,10 @@ namespace osu.Game.Rulesets.Osu.Judgements
{
[Description(@"")]
None,
+
[Description(@"Good")]
Good,
+
[Description(@"Amazing")]
Perfect
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
index 19b627b560..a203e23687 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
- scoreProcessor.Health.ValueChanged += val => { blinds.AnimateClosedness((float)val); };
+ scoreProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); };
}
///
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs
index ba82465a78..2c40d18f1b 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.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.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.Events;
@@ -41,9 +42,9 @@ namespace osu.Game.Rulesets.Osu.Mods
return default_flashlight_size;
}
- protected override void OnComboChange(int newCombo)
+ protected override void OnComboChange(ValueChangedEvent e)
{
- this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(newCombo)), FLASHLIGHT_FADE_DURATION);
+ this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION);
}
protected override string FragmentShader => "CircularFlashlight";
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
new file mode 100644
index 0000000000..65e9eb7a1d
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
@@ -0,0 +1,74 @@
+// 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.Graphics;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Osu.Mods
+{
+ internal class OsuModGrow : Mod, IApplicableToDrawableHitObjects
+ {
+ public override string Name => "Grow";
+
+ public override string Acronym => "GR";
+
+ public override FontAwesome Icon => FontAwesome.fa_arrows_v;
+
+ public override ModType Type => ModType.Fun;
+
+ public override string Description => "Hit them at the right size!";
+
+ public override double ScoreMultiplier => 1;
+
+ public void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ foreach (var drawable in drawables)
+ {
+ switch (drawable)
+ {
+ case DrawableSpinner _:
+ continue;
+ default:
+ drawable.ApplyCustomUpdateState += ApplyCustomState;
+ break;
+ }
+ }
+ }
+
+ protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state)
+ {
+ var h = (OsuHitObject)drawable.HitObject;
+
+ // apply grow effect
+ switch (drawable)
+ {
+ case DrawableSliderHead _:
+ case DrawableSliderTail _:
+ // special cases we should *not* be scaling.
+ break;
+ case DrawableSlider _:
+ case DrawableHitCircle _:
+ {
+ using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
+ drawable.ScaleTo(0.5f).Then().ScaleTo(1, h.TimePreempt, Easing.OutSine);
+ break;
+ }
+ }
+
+ // remove approach circles
+ switch (drawable)
+ {
+ case DrawableHitCircle circle:
+ // we don't want to see the approach circle
+ using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
+ circle.ApproachCircle.Hide();
+ break;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs
new file mode 100644
index 0000000000..571756d056
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.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.Rulesets.Mods;
+
+namespace osu.Game.Rulesets.Osu.Mods
+{
+ public class OsuModTouchDevice : Mod
+ {
+ public override string Name => "Touch Device";
+ public override string Acronym => "TD";
+ public override double ScoreMultiplier => 1;
+
+ public override bool Ranked => true;
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
index 959bf1dc77..9a769ec39c 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
foreach (var drawable in drawables)
{
- var hitObject = (OsuHitObject) drawable.HitObject;
+ var hitObject = (OsuHitObject)drawable.HitObject;
float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2;
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Mods
.MoveTo(originalPosition, moveDuration, Easing.InOutSine);
}
- theta += (float) hitObject.TimeFadeIn / 1000;
+ theta += (float)hitObject.TimeFadeIn / 1000;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/ConnectionRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/ConnectionRenderer.cs
index 0e88e1094e..9106f4c7bd 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/ConnectionRenderer.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/ConnectionRenderer.cs
@@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
///
/// Connects hit objects visually, for example with follow points.
///
- public abstract class ConnectionRenderer : Container
+ public abstract class ConnectionRenderer : LifetimeManagementContainer
where T : HitObject
{
///
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
index 4c0f646ad5..8f9d487d49 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
@@ -12,39 +12,44 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
public class FollowPointRenderer : ConnectionRenderer
{
private int pointDistance = 32;
+
///
/// Determines how much space there is between points.
///
public int PointDistance
{
- get { return pointDistance; }
+ get => pointDistance;
set
{
if (pointDistance == value) return;
+
pointDistance = value;
update();
}
}
private int preEmpt = 800;
+
///
/// Follow points to the next hitobject start appearing for this many milliseconds before an hitobject's end time.
///
public int PreEmpt
{
- get { return preEmpt; }
+ get => preEmpt;
set
{
if (preEmpt == value) return;
+
preEmpt = value;
update();
}
}
private IEnumerable hitObjects;
+
public override IEnumerable HitObjects
{
- get { return hitObjects; }
+ get => hitObjects;
set
{
hitObjects = value;
@@ -56,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
private void update()
{
- Clear();
+ ClearInternal();
if (hitObjects == null)
return;
@@ -86,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPoint fp;
- Add(fp = new FollowPoint
+ AddInternal(fp = new FollowPoint
{
Position = pointStartPosition,
Rotation = rotation,
@@ -107,6 +112,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
fp.Expire(true);
}
}
+
prevHitObject = currHitObject;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index df0769982d..decd0ce073 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -3,8 +3,9 @@
using System;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osuTK;
@@ -27,40 +28,60 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable stackHeightBindable = new Bindable();
private readonly IBindable scaleBindable = new Bindable();
+ public OsuAction? HitAction => circle.HitAction;
+
+ private readonly Container explodeContainer;
+
+ private readonly Container scaleContainer;
+
public DrawableHitCircle(HitCircle h)
: base(h)
{
Origin = Anchor.Centre;
Position = HitObject.StackedPosition;
- Scale = new Vector2(h.Scale);
InternalChildren = new Drawable[]
{
- glow = new GlowPiece(),
- circle = new CirclePiece
+ scaleContainer = new Container
{
- Hit = () =>
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Child = explodeContainer = new Container
{
- if (AllJudged)
- return false;
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Children = new Drawable[]
+ {
+ glow = new GlowPiece(),
+ circle = new CirclePiece
+ {
+ Hit = () =>
+ {
+ if (AllJudged)
+ return false;
- UpdateResult(true);
- return true;
- },
+ UpdateResult(true);
+ return true;
+ },
+ },
+ number = new NumberPiece
+ {
+ Text = (HitObject.IndexInCurrentCombo + 1).ToString(),
+ },
+ ring = new RingPiece(),
+ flash = new FlashPiece(),
+ explode = new ExplodePiece(),
+ ApproachCircle = new ApproachCircle
+ {
+ Alpha = 0,
+ Scale = new Vector2(4),
+ }
+ }
+ }
},
- number = new NumberPiece
- {
- Text = (HitObject.IndexInCurrentCombo + 1).ToString(),
- },
- ring = new RingPiece(),
- flash = new FlashPiece(),
- explode = new ExplodePiece(),
- ApproachCircle = new ApproachCircle
- {
- Alpha = 0,
- Scale = new Vector2(4),
- }
};
//may not be so correct
@@ -72,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
- scaleBindable.BindValueChanged(v => Scale = new Vector2(v));
+ scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
positionBindable.BindTo(HitObject.PositionBindable);
stackHeightBindable.BindTo(HitObject.StackHeightBindable);
@@ -81,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public override Color4 AccentColour
{
- get { return base.AccentColour; }
+ get => base.AccentColour;
set
{
base.AccentColour = value;
@@ -118,6 +139,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
+ ApproachCircle.Expire(true);
}
protected override void UpdateCurrentState(ArmedState state)
@@ -131,6 +153,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Expire(true);
+ circle.HitAction = null;
+
// override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss);
break;
@@ -156,8 +180,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
circle.FadeOut();
number.FadeOut();
- this.FadeOut(800)
- .ScaleTo(Scale * 1.5f, 400, Easing.OutQuad);
+ this.FadeOut(800);
+ explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad);
}
Expire();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 2608706264..10b37af957 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
- AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
+ AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 1a1b0530d8..ff74954552 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -8,10 +8,10 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
-using osu.Game.Configuration;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
@@ -33,6 +33,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable scaleBindable = new Bindable();
private readonly IBindable pathBindable = new Bindable();
+ [Resolved(CanBeNull = true)]
+ private OsuRulesetConfigManager config { get; set; }
+
public DrawableSlider(Slider s)
: base(s)
{
@@ -53,6 +56,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
repeatPoints = new Container { RelativeSizeAxes = Axes.Both },
Ball = new SliderBall(s, this)
{
+ GetInitialHitAction = () => HeadCircle.HitAction,
BypassAutoSizeAxes = Axes.Both,
Scale = new Vector2(s.Scale),
AlwaysPresent = true,
@@ -93,16 +97,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
[BackgroundDependencyLoader]
- private void load(OsuConfigManager config)
+ private void load()
{
- config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn);
- config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut);
+ config?.BindWith(OsuRulesetSetting.SnakingInSliders, Body.SnakingIn);
+ config?.BindWith(OsuRulesetSetting.SnakingOutSliders, Body.SnakingOut);
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
- scaleBindable.BindValueChanged(v =>
+ scaleBindable.BindValueChanged(scale =>
{
- Body.PathWidth = HitObject.Scale * 64;
- Ball.Scale = new Vector2(HitObject.Scale);
+ Body.PathWidth = scale.NewValue * 64;
+ Ball.Scale = new Vector2(scale.NewValue);
});
positionBindable.BindTo(HitObject.PositionBindable);
@@ -114,7 +118,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public override Color4 AccentColour
{
- get { return base.AccentColour; }
+ get => base.AccentColour;
set
{
base.AccentColour = value;
@@ -156,9 +160,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.SkinChanged(skin, allowFallback);
- Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : Body.AccentColour);
- Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : Body.BorderColour);
- Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : Ball.AccentColour);
+ Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? Body.AccentColour;
+ Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Body.BorderColour;
+ Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Ball.AccentColour;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
index e24efc6ffb..66b6f0f9ac 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
@@ -3,7 +3,7 @@
using System;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osuTK;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
index 53f6aa5b76..23c5494cf5 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
@@ -1,7 +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.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index 1105e8525b..b5ce36f889 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -20,7 +20,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public override bool DisplayResult => false;
- public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
+ public DrawableSliderTick(SliderTick sliderTick)
+ : base(sliderTick)
{
Size = new Vector2(16) * sliderTick.Scale;
Origin = Anchor.Centre;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index c411b562e4..789af4f49b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -11,7 +11,7 @@ using osuTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Screens.Ranking;
using osu.Game.Rulesets.Scoring;
@@ -42,7 +42,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Color4 normalColour;
private Color4 completeColour;
- public DrawableSpinner(Spinner s) : base(s)
+ public DrawableSpinner(Spinner s)
+ : base(s)
{
Origin = Anchor.Centre;
Position = s.Position;
@@ -130,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
circle.Colour = colours.BlueDark;
glow.Colour = colours.BlueDark;
- positionBindable.BindValueChanged(v => Position = v);
+ positionBindable.BindValueChanged(pos => Position = pos.NewValue);
positionBindable.BindTo(HitObject.PositionBindable);
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
index 8ed99ca660..8ee065aaea 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
@@ -12,6 +12,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class ApproachCircle : Container
{
+ public override bool RemoveWhenNotAlive => false;
+
public ApproachCircle()
{
Anchor = Anchor.Centre;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
index b7e2748ca9..786cac7198 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
@@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public Func Hit;
+ public OsuAction? HitAction;
+
public CirclePiece()
{
Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2);
@@ -35,7 +37,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
- return IsHovered && (Hit?.Invoke() ?? false);
+ if (IsHovered && (Hit?.Invoke() ?? false))
+ {
+ HitAction = action;
+ return true;
+ }
+
+ break;
}
return false;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
index bc354bc2b6..93ac8748dd 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@@ -17,8 +18,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public string Text
{
- get { return number.Text; }
- set { number.Text = value; }
+ get => number.Text;
+ set => number.Text = value;
}
public NumberPiece()
@@ -42,9 +43,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}, s => s.GetTexture("Play/osu/hitcircle") == null),
number = new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText
{
- Font = @"Venera",
+ Font = OsuFont.Numeric.With(size: 40),
UseFullGlyphHeight = false,
- TextSize = 40,
}, restrictSize: false)
{
Text = @"1"
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index db9fee242a..1b2e2c1f47 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -19,12 +20,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private Color4 accentColour = Color4.Black;
+ public Func GetInitialHitAction;
+
///
/// The colour that is used for the slider ball.
///
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
accentColour = value;
@@ -133,11 +136,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public bool Tracking
{
- get { return tracking; }
+ get => tracking;
private set
{
if (value == tracking)
return;
+
tracking = value;
FollowCircle.ScaleTo(tracking ? 2f : 1, 300, Easing.OutQuint);
@@ -145,20 +149,72 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
}
- private bool canCurrentlyTrack => Time.Current >= slider.StartTime && Time.Current < slider.EndTime;
+ ///
+ /// If the cursor moves out of the ball's radius we still need to be able to receive positional updates to stop tracking.
+ ///
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
+
+ ///
+ /// The point in time after which we can accept any key for tracking. Before this time, we may need to restrict tracking to the key used to hit the head circle.
+ ///
+ /// This is a requirement to stop the case where a player holds down one key (from before the slider) and taps the second key while maintaining full scoring (tracking) of sliders.
+ /// Visually, this special case can be seen below (time increasing from left to right):
+ ///
+ /// Z Z+X Z
+ /// o========o
+ ///
+ /// Without this logic, tracking would continue through the entire slider even though no key hold action is directly attributing to it.
+ ///
+ /// In all other cases, no special handling is required (either key being pressed is allowable as valid tracking).
+ ///
+ /// The reason for storing this as a time value (rather than a bool) is to correctly handle rewind scenarios.
+ ///
+ private double? timeToAcceptAnyKeyAfter;
protected override void Update()
{
base.Update();
- if (Time.Current < slider.EndTime)
+ // from the point at which the head circle is hit, this will be non-null.
+ // it may be null if the head circle was missed.
+ var headCircleHitAction = GetInitialHitAction();
+
+ if (headCircleHitAction == null)
+ timeToAcceptAnyKeyAfter = null;
+
+ var actions = drawableSlider?.OsuActionInputManager?.PressedActions;
+
+ // if the head circle was hit with a specific key, tracking should only occur while that key is pressed.
+ if (headCircleHitAction != null && timeToAcceptAnyKeyAfter == null)
{
- // Make sure to use the base version of ReceivePositionalInputAt so that we correctly check the position.
- Tracking = canCurrentlyTrack
- && lastScreenSpaceMousePosition.HasValue
- && ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value)
- && (drawableSlider?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
+ var otherKey = headCircleHitAction == OsuAction.RightButton ? OsuAction.LeftButton : OsuAction.RightButton;
+
+ // we can return to accepting all keys if the initial head circle key is the *only* key pressed, or all keys have been released.
+ if (actions?.Contains(otherKey) != true)
+ timeToAcceptAnyKeyAfter = Time.Current;
}
+
+ Tracking =
+ // in valid time range
+ Time.Current >= slider.StartTime && Time.Current < slider.EndTime &&
+ // in valid position range
+ lastScreenSpaceMousePosition.HasValue && base.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
+ // valid action
+ (actions?.Any(isValidTrackingAction) ?? false);
+ }
+
+ ///
+ /// Check whether a given user input is a valid tracking action.
+ ///
+ private bool isValidTrackingAction(OsuAction action)
+ {
+ bool headCircleHit = GetInitialHitAction().HasValue;
+
+ // if the head circle was hit, we may not yet be allowed to accept any key, so we must use the initial hit action.
+ if (headCircleHit && (!timeToAcceptAnyKeyAfter.HasValue || Time.Current <= timeToAcceptAnyKeyAfter.Value))
+ return action == GetInitialHitAction();
+
+ return action == OsuAction.LeftButton || action == OsuAction.RightButton;
}
public void UpdateProgress(double completionProgress)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
index 6d2dc220da..b088f1914b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -40,6 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
if (path.AccentColour == value)
return;
+
path.AccentColour = value;
container.ForceRedraw();
@@ -56,6 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
if (path.BorderColour == value)
return;
+
path.BorderColour = value;
container.ForceRedraw();
@@ -105,6 +107,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
if (borderColour == value)
return;
+
borderColour = value;
InvalidateTexture();
@@ -120,6 +123,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
if (accentColour == value)
return;
+
accentColour = value;
InvalidateTexture();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
index 256cd088de..73b184bffe 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
@@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Types;
using osuTK;
@@ -54,18 +54,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
var spanProgress = slider.ProgressAt(completionProgress);
double start = 0;
- double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
+ double end = SnakingIn.Value ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
if (span >= slider.SpanCount() - 1)
{
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
{
start = 0;
- end = SnakingOut ? spanProgress : 1;
+ end = SnakingOut.Value ? spanProgress : 1;
}
else
{
- start = SnakingOut ? spanProgress : 0;
+ start = SnakingOut.Value ? spanProgress : 0;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
index 0d970f4c2c..c982f53c2b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
@@ -15,10 +15,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public Color4 AccentColour
{
- get
- {
- return Disc.Colour;
- }
+ get => Disc.Colour;
set
{
Disc.Colour = value;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
index 4206852b6c..448a2eada7 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
@@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public Color4 AccentColour
{
- get { return background.AccentColour; }
- set { background.AccentColour = value; }
+ get => background.AccentColour;
+ set => background.AccentColour = value;
}
private readonly SpinnerBackground background;
@@ -43,12 +43,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
private bool tracking;
+
public bool Tracking
{
- get { return tracking; }
+ get => tracking;
set
{
if (value == tracking) return;
+
tracking = value;
background.FadeTo(tracking ? tracking_alpha : idle_alpha, 100);
@@ -56,12 +58,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
private bool complete;
+
public bool Complete
{
- get { return complete; }
+ get => complete;
set
{
if (value == complete) return;
+
complete = value;
updateCompleteTick();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs
index e4d09b9306..b1d90c49f6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@@ -23,16 +24,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = @"0",
- Font = @"Venera",
- TextSize = 24
+ Font = OsuFont.Numeric.With(size: 24)
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = @"SPINS PER MINUTE",
- Font = @"Venera",
- TextSize = 12,
+ Font = OsuFont.Numeric.With(size: 12),
Y = 30
}
};
@@ -42,10 +41,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public double SpinsPerMinute
{
- get { return spm; }
+ get => spm;
private set
{
if (value == spm) return;
+
spm = value;
spmText.Text = Math.Truncate(value).ToString(@"#0");
}
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index a2e518ace4..364c182dd4 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -1,7 +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.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osuTK;
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public virtual Vector2 Position
{
- get => PositionBindable;
+ get => PositionBindable.Value;
set => PositionBindable.Value = value;
}
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public int StackHeight
{
- get => StackHeightBindable;
+ get => StackHeightBindable.Value;
set => StackHeightBindable.Value = value;
}
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public float Scale
{
- get => ScaleBindable;
+ get => ScaleBindable.Value;
set => ScaleBindable.Value = value;
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index d47fed354a..345f599b9d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -7,8 +7,8 @@ using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects;
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Caching;
-using osu.Framework.Configuration;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
@@ -262,6 +262,7 @@ namespace osu.Game.Rulesets.Osu.Objects
{
if (nodeIndex < NodeSamples.Count)
return NodeSamples[nodeIndex];
+
return Samples;
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
index 29b00b8d5d..43a2ae0fbb 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
@@ -1,7 +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.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs
index 2dd34d7d40..b9e083d35b 100644
--- a/osu.Game.Rulesets.Osu/OsuInputManager.cs
+++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs
@@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Osu
protected override bool Handle(UIEvent e)
{
if (!AllowUserPresses) return false;
+
return base.Handle(e);
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index 9415adc411..20752517d5 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -16,8 +16,11 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy;
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Osu.Beatmaps;
+using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Difficulty;
using osu.Game.Scoring;
@@ -82,6 +85,9 @@ namespace osu.Game.Rulesets.Osu
if (mods.HasFlag(LegacyMods.Target))
yield return new OsuModTarget();
+
+ if (mods.HasFlag(LegacyMods.TouchDevice))
+ yield return new OsuModTouchDevice();
}
public override IEnumerable GetModsFor(ModType type)
@@ -118,9 +124,11 @@ namespace osu.Game.Rulesets.Osu
new OsuModAutopilot(),
};
case ModType.Fun:
- return new Mod[] {
+ return new Mod[]
+ {
new OsuModTransform(),
new OsuModWiggle(),
+ new OsuModGrow()
};
default:
return new Mod[] { };
@@ -139,12 +147,14 @@ namespace osu.Game.Rulesets.Osu
public override string ShortName => "osu";
- public override RulesetSettingsSubsection CreateSettings() => new OsuSettings(this);
+ public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);
public override int? LegacyID => 0;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
+ public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
+
public OsuRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
index b024ff4b05..b0fb85d7ed 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
@@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Osu.Replays
// Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up.
if (timeDifference > 0 && // Sanity checks
((lastPosition - targetPos).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough
- timeDifference >= 266)) // ... or the beats are slow enough to tap anyway.
+ timeDifference >= 266)) // ... or the beats are slow enough to tap anyway.
{
// Perform eased movement
for (double time = lastFrame.Time + FrameDelay; time < h.StartTime; time += FrameDelay)
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs
index 8bcdb5ee41..9a60f0cafc 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs
@@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Osu.Replays
/// Constants (for spinners).
///
protected static readonly Vector2 SPINNER_CENTRE = OsuPlayfield.BASE_SIZE / 2;
+
protected const float SPIN_RADIUS = 50;
///
@@ -46,6 +47,7 @@ namespace osu.Game.Rulesets.Osu.Replays
#endregion
#region Utilities
+
protected double ApplyModsToTime(double v) => v;
protected double ApplyModsToRate(double v) => v;
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuFramedReplayInputHandler.cs
similarity index 89%
rename from osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
rename to osu.Game.Rulesets.Osu/Replays/OsuFramedReplayInputHandler.cs
index 0e9a906d85..d1ac77857d 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuFramedReplayInputHandler.cs
@@ -11,9 +11,9 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Replays
{
- public class OsuReplayInputHandler : FramedReplayInputHandler
+ public class OsuFramedReplayInputHandler : FramedReplayInputHandler
{
- public OsuReplayInputHandler(Replay replay)
+ public OsuFramedReplayInputHandler(Replay replay)
: base(replay)
{
}
diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu
new file mode 100644
index 0000000000..bf345811a2
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu
@@ -0,0 +1,179 @@
+osu file format v14
+
+[General]
+StackLeniency: 0.3
+Mode: 0
+
+[Difficulty]
+CircleSize:4
+OverallDifficulty:7
+ApproachRate:8.3
+SliderMultiplier:1.6
+SliderTickRate:1
+
+[TimingPoints]
+500,500,4,2,1,50,1,0
+62500,-500,4,2,1,50,0,0
+71000,-100,4,2,1,50,0,0
+
+[HitObjects]
+// Circles spaced 1 beat apart, with increasing jump distance
+126,112,500,5,0,0:0:0:0:
+130,155,1000,1,0,0:0:0:0:
+131,269,1500,1,0,0:0:0:0:
+341,269,2000,1,0,0:0:0:0:
+113,95,2500,1,0,0:0:0:0:
+
+// Circles spaced 1/2 beat apart, with increasing jump distance
+108,104,3500,5,0,0:0:0:0:
+110,145,3750,1,0,0:0:0:0:
+115,262,4000,1,0,0:0:0:0:
+285,265,4250,1,0,0:0:0:0:
+458,48,4500,1,0,0:0:0:0:
+35,199,4750,1,0,0:0:0:0:
+251,340,5000,1,0,0:0:0:0:
+20,352,5250,1,0,0:0:0:0:
+426,62,5500,1,0,0:0:0:0:
+
+// Circles spaced 1/4 beat apart, with increasing jump distances
+211,138,6500,5,0,0:0:0:0:
+99,256,6625,1,0,0:0:0:0:
+68,129,6750,1,0,0:0:0:0:
+371,340,6875,1,0,0:0:0:0:
+241,219,7000,1,0,0:0:0:0:
+252,148,7125,1,0,0:0:0:0:
+434,97,7250,1,0,0:0:0:0:
+40,38,7375,1,0,0:0:0:0:
+114,334,7500,1,0,0:0:0:0:
+301,19,7625,1,0,0:0:0:0:
+441,241,7750,1,0,0:0:0:0:
+121,91,7875,1,0,0:0:0:0:
+270,384,8000,1,0,0:0:0:0:
+488,92,8125,1,0,0:0:0:0:
+332,82,8250,1,0,0:0:0:0:
+108,240,8375,1,0,0:0:0:0:
+281,268,8500,1,0,0:0:0:0:
+
+// Constant spaced circles spaced 1/2 beat apart, small jump distances, changing angles
+252,191,9500,5,0,0:0:0:0:
+356,191,9750,1,0,0:0:0:0:
+311,268,10000,1,0,0:0:0:0:
+190,270,10250,1,0,0:0:0:0:
+107,199,10500,1,0,0:0:0:0:
+172,105,10750,1,0,0:0:0:0:
+297,102,11000,1,0,0:0:0:0:
+373,178,11250,1,0,0:0:0:0:
+252,195,11500,1,0,0:0:0:0:
+
+// Constant spaced circles spaced 1/2 beat apart, large jump distances, changing angles
+140,187,12500,5,0,0:0:0:0:
+451,331,12750,1,0,0:0:0:0:
+46,338,13000,1,0,0:0:0:0:
+204,50,13250,1,0,0:0:0:0:
+464,162,13500,1,0,0:0:0:0:
+252,346,13750,1,0,0:0:0:0:
+13,175,14000,1,0,0:0:0:0:
+488,181,14250,1,0,0:0:0:0:
+251,187,14500,1,0,0:0:0:0:
+
+// Constant spaced circles spaced 1/4 beat apart, small jump distances, changing angles
+188,192,15500,5,0,0:0:0:0:
+298,194,15625,1,0,0:0:0:0:
+317,84,15750,1,0,0:0:0:0:
+185,85,15875,1,0,0:0:0:0:
+77,200,16000,1,0,0:0:0:0:
+184,303,16125,1,0,0:0:0:0:
+295,225,16250,1,0,0:0:0:0:
+300,84,16375,1,0,0:0:0:0:
+144,82,16500,1,0,0:0:0:0:
+141,215,16625,1,0,0:0:0:0:
+314,184,16750,1,0,0:0:0:0:
+188,192,16875,1,0,0:0:0:0:
+188,192,17000,1,0,0:0:0:0:
+
+// Constant spaced circles spaced 1/4 beat apart, large jump distances, changing angles
+97,192,18000,5,0,0:0:0:0:
+336,38,18125,1,0,0:0:0:0:
+440,322,18250,1,0,0:0:0:0:
+39,331,18375,1,0,0:0:0:0:
+98,39,18500,1,0,0:0:0:0:
+460,179,18625,1,0,0:0:0:0:
+245,338,18750,1,0,0:0:0:0:
+12,184,18875,1,0,0:0:0:0:
+250,41,19000,1,0,0:0:0:0:
+265,193,19125,1,0,0:0:0:0:
+486,22,19250,1,0,0:0:0:0:
+411,205,19375,1,0,0:0:0:0:
+107,198,19500,1,0,0:0:0:0:
+
+// Short sliders spaced 1 beat apart
+28,108,20500,2,0,L|196:107,1,160
+25,177,21500,2,0,L|193:176,1,160
+26,308,22500,2,0,L|194:307,1,160
+320,89,23500,2,0,L|488:88,1,160
+
+// Short sliders spaced 1/2 beat apart
+28,108,25000,6,0,L|196:107,1,160
+27,173,25750,2,0,L|195:172,1,160
+25,292,26500,2,0,L|193:291,1,160
+340,213,27250,2,0,L|508:212,1,160
+21,44,28000,2,0,L|189:43,1,160
+
+// Short sliders spaced 1/4 beat apart
+28,108,29500,6,0,L|196:107,1,160
+30,169,30125,2,0,L|198:168,1,160
+35,282,30750,2,0,L|203:281,1,160
+327,286,31375,2,0,L|495:285,1,160
+51,61,32000,2,0,L|219:60,1,160
+
+// Large, medium-paced slider shapes
+// PerfectCurve
+66,86,33500,6,0,P|246:348|427:44,1,800
+66,86,36500,2,0,P|246:348|427:44,1,800
+66,86,39500,2,0,P|246:348|427:44,1,800
+// Linear
+66,72,42500,2,0,B|419:65|419:65|66:316|66:316|426:318,1,1120
+66,72,46500,2,0,B|419:65|419:65|66:316|66:316|426:318,1,1120
+66,72,50500,2,0,B|419:65|419:65|66:316|66:316|426:318,1,1120
+// Bezier
+76,287,54500,2,0,B|440:325|138:128|470:302|500:30|130:85|66:82,1,640
+76,287,57000,2,0,B|440:325|138:128|470:302|500:30|130:85|66:82,1,640
+76,287,59500,2,0,B|440:325|138:128|470:302|500:30|130:85|66:82,1,640
+
+// Large slow slider with many ticks
+81,170,62500,6,0,P|263:78|168:268,1,480
+
+// Fast slider with many repeats
+102,152,71000,6,0,L|175:153,18,64
+
+// Slider-circle combos, spaced 1/2 beat apart
+106,204,75500,6,0,P|275:33|171:304,1,800
+255,179,78250,1,0,0:0:0:0:
+106,204,78500,2,0,P|275:33|171:304,1,800
+255,179,81250,1,0,0:0:0:0:
+106,204,81500,2,0,P|275:33|171:304,1,800
+
+// Circle-spinner combos, spaced 1/2 beat apart
+82,69,85000,5,0,0:0:0:0:
+256,192,85250,8,0,86000,0:0:0:0:
+384,189,86250,5,0,0:0:0:0:
+256,192,86500,12,0,87000,0:0:0:0:
+
+// Spinner-spinner combos, spaced 1/2 beat apart
+256,192,88000,12,0,89000,0:0:0:0:
+256,192,89250,12,0,90250,0:0:0:0:
+256,192,90500,12,0,91500,0:0:0:0:
+256,192,91750,12,0,92750,0:0:0:0:
+256,192,93000,12,0,94000,0:0:0:0:
+
+// Slider-spinner combos, spaced 1/2 beat apart
+49,89,95000,6,0,L|214:87,1,160
+256,192,95625,12,0,96500,0:0:0:0:
+12,299,96625,6,0,L|177:297,1,160
+256,192,97250,12,0,98125,0:0:0:0:
+295,107,98250,6,0,L|460:105,1,160
+256,192,98875,12,0,99750,0:0:0:0:
+279,325,99875,6,0,L|444:323,1,160
+256,192,100500,12,0,101375,0:0:0:0:
+197,197,101500,6,0,L|362:195,1,160
+256,192,102125,12,0,103000,0:0:0:0:
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index 777588d6d7..4f97cc0da5 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -74,6 +74,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
- protected override HitWindows CreateHitWindows() => new OsuHitWindows();
+ public override HitWindows CreateHitWindows() => new OsuHitWindows();
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 03030121d3..0f8a0ce1ae 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -255,10 +255,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
[VertexMember(2, VertexAttribPointerType.Float)]
public Vector2 Position;
+
[VertexMember(4, VertexAttribPointerType.Float)]
public Color4 Colour;
+
[VertexMember(2, VertexAttribPointerType.Float)]
public Vector2 TexturePosition;
+
[VertexMember(1, VertexAttribPointerType.Float)]
public float Time;
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
index 3fef769174..ef126cdf7d 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
}
[BackgroundDependencyLoader]
- private void load(OsuConfigManager config, IBindableBeatmap beatmap)
+ private void load(OsuConfigManager config, IBindable beatmap)
{
InternalChild = expandTarget = new Container
{
@@ -183,13 +183,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
};
this.beatmap.BindTo(beatmap);
- this.beatmap.ValueChanged += v => calculateScale();
+ this.beatmap.ValueChanged += _ => calculateScale();
cursorScale = config.GetBindable(OsuSetting.GameplayCursorSize);
- cursorScale.ValueChanged += v => calculateScale();
+ cursorScale.ValueChanged += _ => calculateScale();
autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize);
- autoCursorScale.ValueChanged += v => calculateScale();
+ autoCursorScale.ValueChanged += _ => calculateScale();
calculateScale();
}
@@ -198,7 +198,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
float scale = (float)cursorScale.Value;
- if (autoCursorScale && beatmap.Value != null)
+ 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 *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
@@ -213,6 +213,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
public void Expand()
{
if (!cursorExpand) return;
+
expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad);
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index c4097ccb46..2db2b45540 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.UI
{
public class OsuPlayfield : Playfield
{
- private readonly Container approachCircles;
+ private readonly ApproachCircleProxyContainer approachCircles;
private readonly JudgementContainer judgementLayer;
private readonly ConnectionRenderer connectionLayer;
@@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.UI
Depth = 1,
},
HitObjectContainer,
- approachCircles = new Container
+ approachCircles = new ApproachCircleProxyContainer
{
RelativeSizeAxes = Axes.Both,
Depth = -1,
@@ -58,13 +58,26 @@ namespace osu.Game.Rulesets.Osu.UI
{
h.OnNewResult += onNewResult;
- var c = h as IDrawableHitObjectWithProxiedApproach;
- if (c != null)
- approachCircles.Add(c.ProxiedLayer.CreateProxy());
+ if (h is IDrawableHitObjectWithProxiedApproach c)
+ {
+ var original = c.ProxiedLayer;
+
+ // Hitobjects only have lifetimes set on LoadComplete. For nested hitobjects (e.g. SliderHeads), this only happens when the parenting slider becomes visible.
+ // This delegation is required to make sure that the approach circles for those not-yet-loaded objects aren't added prematurely.
+ original.OnLoadComplete += addApproachCircleProxy;
+ }
base.Add(h);
}
+ private void addApproachCircleProxy(Drawable d)
+ {
+ var proxy = d.CreateProxy();
+ proxy.LifetimeStart = d.LifetimeStart;
+ proxy.LifetimeEnd = d.LifetimeEnd;
+ approachCircles.Add(proxy);
+ }
+
public override void PostProcess()
{
connectionLayer.HitObjects = HitObjectContainer.Objects.Select(d => d.HitObject).OfType();
@@ -72,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.UI
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!judgedObject.DisplayResult || !DisplayJudgements)
+ if (!judgedObject.DisplayResult || !DisplayJudgements.Value)
return;
DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject)
@@ -86,5 +99,10 @@ namespace osu.Game.Rulesets.Osu.UI
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos);
+
+ private class ApproachCircleProxyContainer : LifetimeManagementContainer
+ {
+ public void Add(Drawable approachCircleProxy) => AddInternal(approachCircleProxy);
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
index 86e5f8467d..85b72cbb5b 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
@@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Replays;
@@ -20,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.UI
{
public class OsuRulesetContainer : RulesetContainer
{
+ protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
+
public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
@@ -46,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.UI
return null;
}
- protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
+ protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay);
public override double GameplayStartTime
{
diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettings.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs
similarity index 64%
rename from osu.Game.Rulesets.Osu/UI/OsuSettings.cs
rename to osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs
index 3620145ced..ce3432c73d 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuSettings.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs
@@ -3,34 +3,36 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Game.Configuration;
using osu.Game.Overlays.Settings;
+using osu.Game.Rulesets.Osu.Configuration;
namespace osu.Game.Rulesets.Osu.UI
{
- public class OsuSettings : RulesetSettingsSubsection
+ public class OsuSettingsSubsection : RulesetSettingsSubsection
{
protected override string Header => "osu!";
- public OsuSettings(Ruleset ruleset)
+ public OsuSettingsSubsection(Ruleset ruleset)
: base(ruleset)
{
}
[BackgroundDependencyLoader]
- private void load(OsuConfigManager config)
+ private void load()
{
+ var config = (OsuRulesetConfigManager)Config;
+
Children = new Drawable[]
{
new SettingsCheckbox
{
LabelText = "Snaking in sliders",
- Bindable = config.GetBindable(OsuSetting.SnakingInSliders)
+ Bindable = config.GetBindable(OsuRulesetSetting.SnakingInSliders)
},
new SettingsCheckbox
{
LabelText = "Snaking out sliders",
- Bindable = config.GetBindable(OsuSetting.SnakingOutSliders)
+ Bindable = config.GetBindable(OsuRulesetSetting.SnakingOutSliders)
},
};
}
diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj
index 2ab0633786..d2817b743c 100644
--- a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj
@@ -39,10 +39,6 @@
{F167E17A-7DE6-4AF5-B920-A5112296C695}
osu.Game.Rulesets.Taiko
-
- {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}
- osu.Game.Resources
-
diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs
new file mode 100644
index 0000000000..e7b6d8615b
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs
@@ -0,0 +1,25 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Taiko.Difficulty;
+using osu.Game.Tests.Beatmaps;
+
+namespace osu.Game.Rulesets.Taiko.Tests
+{
+ public class TaikoDifficultyCalculatorTest : DifficultyCalculatorTest
+ {
+ protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
+
+ [TestCase(2.9811338051242915d, "diffcalc-test")]
+ [TestCase(2.9811338051242915d, "diffcalc-test-strong")]
+ public void Test(double expected, string name)
+ => base.Test(expected, name);
+
+ protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap);
+
+ protected override Ruleset CreateRuleset() => new TaikoRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs
index 2c02649102..00e1b649d9 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs
@@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Ruleset = new TaikoRuleset().RulesetInfo
},
ControlPointInfo = controlPointInfo
- });
+ }, Clock);
Add(playfieldContainer = new Container
{
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs
new file mode 100644
index 0000000000..24345275c1
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs
@@ -0,0 +1,20 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Taiko.Objects;
+
+namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
+{
+ public class TaikoDifficultyHitObject : DifficultyHitObject
+ {
+ public readonly bool HasTypeChange;
+
+ public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate)
+ : base(hitObject, lastObject, clockRate)
+ {
+ HasTypeChange = lastObject is RimHit != hitObject is RimHit;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs
new file mode 100644
index 0000000000..c6fe273b50
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs
@@ -0,0 +1,95 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
+using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Taiko.Objects;
+
+namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
+{
+ public class Strain : Skill
+ {
+ private const double rhythm_change_base_threshold = 0.2;
+ private const double rhythm_change_base = 2.0;
+
+ protected override double SkillMultiplier => 1;
+ protected override double StrainDecayBase => 0.3;
+
+ private ColourSwitch lastColourSwitch = ColourSwitch.None;
+
+ private int sameColourCount = 1;
+
+ protected override double StrainValueOf(DifficultyHitObject current)
+ {
+ double addition = 1;
+
+ // We get an extra addition if we are not a slider or spinner
+ if (current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000)
+ {
+ if (hasColourChange(current))
+ addition += 0.75;
+
+ if (hasRhythmChange(current))
+ addition += 1;
+ }
+ else
+ {
+ lastColourSwitch = ColourSwitch.None;
+ sameColourCount = 1;
+ }
+
+ double additionFactor = 1;
+
+ // Scale the addition factor linearly from 0.4 to 1 for DeltaTime from 0 to 50
+ if (current.DeltaTime < 50)
+ additionFactor = 0.4 + 0.6 * current.DeltaTime / 50;
+
+ return additionFactor * addition;
+ }
+
+ private bool hasRhythmChange(DifficultyHitObject current)
+ {
+ // We don't want a division by zero if some random mapper decides to put two HitObjects at the same time.
+ if (current.DeltaTime == 0 || Previous.Count == 0 || Previous[0].DeltaTime == 0)
+ return false;
+
+ double timeElapsedRatio = Math.Max(Previous[0].DeltaTime / current.DeltaTime, current.DeltaTime / Previous[0].DeltaTime);
+
+ if (timeElapsedRatio >= 8)
+ return false;
+
+ double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0;
+
+ return difference > rhythm_change_base_threshold && difference < 1 - rhythm_change_base_threshold;
+ }
+
+ private bool hasColourChange(DifficultyHitObject current)
+ {
+ var taikoCurrent = (TaikoDifficultyHitObject)current;
+
+ if (!taikoCurrent.HasTypeChange)
+ {
+ sameColourCount++;
+ return false;
+ }
+
+ var oldColourSwitch = lastColourSwitch;
+ var newColourSwitch = sameColourCount % 2 == 0 ? ColourSwitch.Even : ColourSwitch.Odd;
+
+ lastColourSwitch = newColourSwitch;
+ sameColourCount = 1;
+
+ // We only want a bonus if the parity of the color switch changes
+ return oldColourSwitch != ColourSwitch.None && oldColourSwitch != newColourSwitch;
+ }
+
+ private enum ColourSwitch
+ {
+ None,
+ Even,
+ Odd
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
index 3770f9601a..75d3807bba 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Difficulty;
-using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
@@ -10,10 +9,5 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
{
public double GreatHitWindow;
public int MaxCombo;
-
- public TaikoDifficultyAttributes(Mod[] mods, double starRating)
- : base(mods, starRating)
- {
- }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
index 16cebb0d96..685ad9949b 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
@@ -1,137 +1,51 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Taiko.Difficulty.Skills;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
- internal class TaikoDifficultyCalculator : DifficultyCalculator
+ public class TaikoDifficultyCalculator : DifficultyCalculator
{
private const double star_scaling_factor = 0.04125;
- ///
- /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
- /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
- /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
- ///
- private const double strain_step = 400;
-
- ///
- /// The weighting of each strain value decays to this number * it's previous value
- ///
- private const double decay_weight = 0.9;
-
public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
- protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
+ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
- if (!beatmap.HitObjects.Any())
- return new TaikoDifficultyAttributes(mods, 0);
+ if (beatmap.HitObjects.Count == 0)
+ return new TaikoDifficultyAttributes { Mods = mods };
- var difficultyHitObjects = new List();
-
- foreach (var hitObject in beatmap.HitObjects)
- difficultyHitObjects.Add(new TaikoHitObjectDifficulty((TaikoHitObject)hitObject));
-
- // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
- difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
-
- if (!calculateStrainValues(difficultyHitObjects, timeRate))
- return new DifficultyAttributes(mods, 0);
-
- double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
-
- return new TaikoDifficultyAttributes(mods, starRating)
+ return new TaikoDifficultyAttributes
{
- // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
- GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate,
- MaxCombo = beatmap.HitObjects.Count(h => h is Hit)
+ StarRating = skills.Single().DifficultyValue() * star_scaling_factor,
+ Mods = mods,
+ // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
+ GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
+ MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
};
}
- private bool calculateStrainValues(List objects, double timeRate)
+ protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
{
- // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
- using (var hitObjectsEnumerator = objects.GetEnumerator())
- {
- if (!hitObjectsEnumerator.MoveNext()) return false;
-
- TaikoHitObjectDifficulty current = hitObjectsEnumerator.Current;
-
- // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
- while (hitObjectsEnumerator.MoveNext())
- {
- var next = hitObjectsEnumerator.Current;
- next?.CalculateStrains(current, timeRate);
- current = next;
- }
-
- return true;
- }
+ for (int i = 1; i < beatmap.HitObjects.Count; i++)
+ yield return new TaikoDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate);
}
- private double calculateDifficulty(List objects, double timeRate)
- {
- double actualStrainStep = strain_step * timeRate;
-
- // Find the highest strain value within each strain step
- List highestStrains = new List();
- double intervalEndTime = actualStrainStep;
- double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
-
- TaikoHitObjectDifficulty previousHitObject = null;
- foreach (var hitObject in objects)
- {
- // While we are beyond the current interval push the currently available maximum to our strain list
- while (hitObject.BaseHitObject.StartTime > intervalEndTime)
- {
- highestStrains.Add(maximumStrain);
-
- // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
- // until the beginning of the next interval.
- if (previousHitObject == null)
- {
- maximumStrain = 0;
- }
- else
- {
- double decay = Math.Pow(TaikoHitObjectDifficulty.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
- maximumStrain = previousHitObject.Strain * decay;
- }
-
- // Go to the next time interval
- intervalEndTime += actualStrainStep;
- }
-
- // Obtain maximum strain
- maximumStrain = Math.Max(hitObject.Strain, maximumStrain);
-
- previousHitObject = hitObject;
- }
-
- // Build the weighted sum over the highest strains for each interval
- double difficulty = 0;
- double weight = 1;
- highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
-
- foreach (double strain in highestStrains)
- {
- difficulty += weight * strain;
- weight *= decay_weight;
- }
-
- return difficulty;
- }
+ protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] { new Strain() };
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
{
diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs
index c7e6771b80..b99ec57166 100644
--- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs
+++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.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.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
@@ -48,9 +49,9 @@ namespace osu.Game.Rulesets.Taiko.Mods
return default_flashlight_size;
}
- protected override void OnComboChange(int newCombo)
+ protected override void OnComboChange(ValueChangedEvent e)
{
- this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(newCombo)), FLASHLIGHT_FADE_DURATION);
+ this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION);
}
protected override string FragmentShader => "CircularFlashlight";
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index d6a598fbec..9211eccc40 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -229,6 +229,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
// Ensure alternating centre and rim hits
if (lastWasCentre == isCentre)
return false;
+
lastWasCentre = isCentre;
UpdateResult(true);
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index 38bf877040..5f755c7cc3 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -49,6 +49,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected void ProxyContent()
{
if (isProxied) return;
+
isProxied = true;
nonProxiedContent.Remove(Content);
@@ -62,6 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected void UnproxyContent()
{
if (!isProxied) return;
+
isProxied = false;
proxiedContent.Remove(Content);
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
index 5369499dbc..53dbe5d08e 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
///
public override Color4 AccentColour
{
- get { return base.AccentColour; }
+ get => base.AccentColour;
set
{
base.AccentColour = value;
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
///
public override bool KiaiMode
{
- get { return base.KiaiMode; }
+ get => base.KiaiMode;
set
{
base.KiaiMode = value;
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs
index dd6a1a5021..773e3ae907 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs
@@ -11,26 +11,25 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
public class TaikoPiece : BeatSyncedContainer, IHasAccentColour
{
private Color4 accentColour;
+
///
/// The colour of the inner circle and outer glows.
///
public virtual Color4 AccentColour
{
- get { return accentColour; }
- set { accentColour = value; }
+ get => accentColour;
+ set => accentColour = value;
}
private bool kiaiMode;
+
///
/// Whether Kiai mode effects are enabled for this circle piece.
///
public virtual bool KiaiMode
{
- get { return kiaiMode; }
- set
- {
- kiaiMode = value;
- }
+ get => kiaiMode;
+ set => kiaiMode = value;
}
public TaikoPiece()
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs
index d625047d29..83cf7a64ec 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs
@@ -23,9 +23,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
private const float tick_size = 0.35f;
private bool filled;
+
public bool Filled
{
- get { return filled; }
+ get => filled;
set
{
filled = value;
diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
index 9d511daae4..befa728570 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
@@ -19,7 +19,10 @@ namespace osu.Game.Rulesets.Taiko.Objects
///
public int RequiredHits = 10;
- public override bool IsStrong { set => throw new NotSupportedException($"{nameof(Swell)} cannot be a strong hitobject."); }
+ public override bool IsStrong
+ {
+ set => throw new NotSupportedException($"{nameof(Swell)} cannot be a strong hitobject.");
+ }
protected override void CreateNestedHitObjects()
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs
deleted file mode 100644
index 46dd7aa87f..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs
+++ /dev/null
@@ -1,127 +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;
-
-namespace osu.Game.Rulesets.Taiko.Objects
-{
- internal class TaikoHitObjectDifficulty
- {
- ///
- /// Factor by how much individual / overall strain decays per second.
- ///
- ///
- /// These values are results of tweaking a lot and taking into account general feedback.
- ///
- internal const double DECAY_BASE = 0.30;
-
- private const double type_change_bonus = 0.75;
- private const double rhythm_change_bonus = 1.0;
- private const double rhythm_change_base_threshold = 0.2;
- private const double rhythm_change_base = 2.0;
-
- internal TaikoHitObject BaseHitObject;
-
- ///
- /// Measures note density in a way
- ///
- internal double Strain = 1;
-
- private double timeElapsed;
- private int sameTypeSince = 1;
-
- private bool isRim => BaseHitObject is RimHit;
-
- public TaikoHitObjectDifficulty(TaikoHitObject baseHitObject)
- {
- BaseHitObject = baseHitObject;
- }
-
- internal void CalculateStrains(TaikoHitObjectDifficulty previousHitObject, double timeRate)
- {
- // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make.
- // See Taiko feedback thread.
- timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
- double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000);
-
- double addition = 1;
-
- // Only if we are no slider or spinner we get an extra addition
- if (previousHitObject.BaseHitObject is Hit && BaseHitObject is Hit
- && BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime < 1000) // And we only want to check out hitobjects which aren't so far in the past
- {
- addition += typeChangeAddition(previousHitObject);
- addition += rhythmChangeAddition(previousHitObject);
- }
-
- double additionFactor = 1.0;
- // Scale AdditionFactor linearly from 0.4 to 1 for TimeElapsed from 0 to 50
- if (timeElapsed < 50.0)
- additionFactor = 0.4 + 0.6 * timeElapsed / 50.0;
-
- Strain = previousHitObject.Strain * decay + addition * additionFactor;
- }
-
- private TypeSwitch lastTypeSwitchEven = TypeSwitch.None;
- private double typeChangeAddition(TaikoHitObjectDifficulty previousHitObject)
- {
- // If we don't have the same hit type, trigger a type change!
- if (previousHitObject.isRim != isRim)
- {
- lastTypeSwitchEven = previousHitObject.sameTypeSince % 2 == 0 ? TypeSwitch.Even : TypeSwitch.Odd;
-
- // We only want a bonus if the parity of the type switch changes!
- switch (previousHitObject.lastTypeSwitchEven)
- {
- case TypeSwitch.Even:
- if (lastTypeSwitchEven == TypeSwitch.Odd)
- return type_change_bonus;
- break;
- case TypeSwitch.Odd:
- if (lastTypeSwitchEven == TypeSwitch.Even)
- return type_change_bonus;
- break;
- }
- }
- // No type change? Increment counter and keep track of last type switch
- else
- {
- lastTypeSwitchEven = previousHitObject.lastTypeSwitchEven;
- sameTypeSince = previousHitObject.sameTypeSince + 1;
- }
-
- return 0;
- }
-
- private double rhythmChangeAddition(TaikoHitObjectDifficulty previousHitObject)
- {
- // We don't want a division by zero if some random mapper decides to put 2 HitObjects at the same time.
- if (timeElapsed == 0 || previousHitObject.timeElapsed == 0)
- return 0;
-
- double timeElapsedRatio = Math.Max(previousHitObject.timeElapsed / timeElapsed, timeElapsed / previousHitObject.timeElapsed);
-
- if (timeElapsedRatio >= 8)
- return 0;
-
- double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0;
-
- if (isWithinChangeThreshold(difference))
- return rhythm_change_bonus;
-
- return 0;
- }
-
- private bool isWithinChangeThreshold(double value)
- {
- return value > rhythm_change_base_threshold && value < 1 - rhythm_change_base_threshold;
- }
-
- private enum TypeSwitch
- {
- None,
- Even,
- Odd
- }
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test-strong.osu b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test-strong.osu
new file mode 100644
index 0000000000..33510eceb7
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test-strong.osu
@@ -0,0 +1,257 @@
+osu file format v14
+
+[General]
+Mode: 1
+
+[Difficulty]
+CircleSize:4
+OverallDifficulty:7
+ApproachRate:8.3
+SliderMultiplier:1.6
+SliderTickRate:1
+
+[TimingPoints]
+500,500,4,2,1,50,1,0
+62500,-500,4,2,1,50,0,0
+71000,-100,4,2,1,50,0,0
+
+[HitObjects]
+// Same as diffcalc-test with finishers on every note
+142,122,0,5,4,0:0:0:0:
+142,122,125,1,4,0:0:0:0:
+142,122,250,1,4,0:0:0:0:
+142,122,375,1,4,0:0:0:0:
+142,122,500,1,4,0:0:0:0:
+142,122,625,1,4,0:0:0:0:
+142,122,750,1,4,0:0:0:0:
+142,122,875,1,4,0:0:0:0:
+142,122,1000,1,4,0:0:0:0:
+142,122,1125,1,4,0:0:0:0:
+142,122,1250,1,4,0:0:0:0:
+142,122,1375,1,4,0:0:0:0:
+142,122,1500,1,4,0:0:0:0:
+119,106,2500,1,6,0:0:0:0:
+119,106,2625,1,6,0:0:0:0:
+119,106,2750,1,6,0:0:0:0:
+119,106,2875,1,6,0:0:0:0:
+119,106,3000,1,6,0:0:0:0:
+119,106,3125,1,6,0:0:0:0:
+119,106,3250,1,6,0:0:0:0:
+119,106,3375,1,6,0:0:0:0:
+119,106,3500,1,6,0:0:0:0:
+119,106,3625,1,6,0:0:0:0:
+119,106,3750,1,6,0:0:0:0:
+119,106,3875,1,6,0:0:0:0:
+119,106,4000,1,6,0:0:0:0:
+136,90,5000,1,4,0:0:0:0:
+136,90,5125,1,6,0:0:0:0:
+136,90,5250,1,4,0:0:0:0:
+136,90,5375,1,6,0:0:0:0:
+136,90,5500,1,4,0:0:0:0:
+136,90,5625,1,6,0:0:0:0:
+136,90,5750,1,4,0:0:0:0:
+136,90,5875,1,6,0:0:0:0:
+136,90,6000,1,4,0:0:0:0:
+136,90,6125,1,6,0:0:0:0:
+136,90,6250,1,4,0:0:0:0:
+136,90,6375,1,6,0:0:0:0:
+136,90,6500,1,4,0:0:0:0:
+86,113,7500,1,4,0:0:0:0:
+86,113,7625,1,4,0:0:0:0:
+86,113,7750,1,6,0:0:0:0:
+86,113,7875,1,6,0:0:0:0:
+86,113,8000,1,4,0:0:0:0:
+86,113,8125,1,4,0:0:0:0:
+86,113,8250,1,6,0:0:0:0:
+86,113,8375,1,6,0:0:0:0:
+86,113,8500,1,4,0:0:0:0:
+86,113,8625,1,4,0:0:0:0:
+86,113,8750,1,6,0:0:0:0:
+86,113,8875,1,6,0:0:0:0:
+86,113,9000,1,4,0:0:0:0:
+146,90,10000,1,4,0:0:0:0:
+146,90,10125,1,4,0:0:0:0:
+146,90,10250,1,4,0:0:0:0:
+146,90,10375,1,6,0:0:0:0:
+146,90,10500,1,6,0:0:0:0:
+146,90,10625,1,6,0:0:0:0:
+146,90,10750,1,4,0:0:0:0:
+146,90,10875,1,4,0:0:0:0:
+146,90,11000,1,4,0:0:0:0:
+146,90,11125,1,6,0:0:0:0:
+146,90,11250,1,6,0:0:0:0:
+146,90,11375,1,6,0:0:0:0:
+146,90,11500,1,4,0:0:0:0:
+146,90,11625,1,4,0:0:0:0:
+146,90,11750,1,4,0:0:0:0:
+146,90,11875,1,6,0:0:0:0:
+146,90,12000,1,6,0:0:0:0:
+146,90,12125,1,6,0:0:0:0:
+146,90,12250,1,4,0:0:0:0:
+146,90,12375,1,4,0:0:0:0:
+146,90,12500,1,4,0:0:0:0:
+69,99,13500,1,4,0:0:0:0:
+69,99,13625,1,4,0:0:0:0:
+69,99,13750,1,4,0:0:0:0:
+69,99,13875,1,6,0:0:0:0:
+69,99,14000,1,4,0:0:0:0:
+69,99,14125,1,4,0:0:0:0:
+69,99,14250,1,4,0:0:0:0:
+69,99,14375,1,6,0:0:0:0:
+69,99,14500,1,4,0:0:0:0:
+69,99,14625,1,4,0:0:0:0:
+69,99,14750,1,4,0:0:0:0:
+69,99,14875,1,6,0:0:0:0:
+69,99,15000,1,4,0:0:0:0:
+69,99,15125,1,4,0:0:0:0:
+69,99,15250,1,4,0:0:0:0:
+69,99,15375,1,6,0:0:0:0:
+69,99,15500,1,4,0:0:0:0:
+83,89,16500,1,4,0:0:0:0:
+83,89,16625,1,6,0:0:0:0:
+83,89,16750,1,6,0:0:0:0:
+83,89,16875,1,4,0:0:0:0:
+83,89,17000,1,4,0:0:0:0:
+83,89,17125,1,4,0:0:0:0:
+83,89,17250,1,6,0:0:0:0:
+83,89,17375,1,6,0:0:0:0:
+83,89,17500,1,6,0:0:0:0:
+83,89,17625,1,6,0:0:0:0:
+83,89,17750,1,4,0:0:0:0:
+83,89,17875,1,4,0:0:0:0:
+83,89,18000,1,4,0:0:0:0:
+83,89,18125,1,4,0:0:0:0:
+83,89,18250,1,4,0:0:0:0:
+83,89,18375,1,6,0:0:0:0:
+83,89,18500,1,6,0:0:0:0:
+83,89,18625,1,6,0:0:0:0:
+83,89,18750,1,6,0:0:0:0:
+83,89,18875,1,4,0:0:0:0:
+83,89,19000,1,4,0:0:0:0:
+83,89,19125,1,4,0:0:0:0:
+83,89,19250,1,4,0:0:0:0:
+83,89,19375,1,6,0:0:0:0:
+83,89,19500,1,6,0:0:0:0:
+83,89,19625,1,4,0:0:0:0:
+84,122,20500,1,4,0:0:0:0:
+84,122,20625,2,4,L|217:123,1,120
+84,122,21125,1,4,0:0:0:0:
+84,122,21250,2,4,L|217:123,1,120
+84,122,21750,1,4,0:0:0:0:
+84,122,21875,2,4,L|217:123,1,120
+84,122,22375,1,4,0:0:0:0:
+84,122,22500,2,4,L|217:123,1,120
+84,122,23000,1,4,0:0:0:0:
+84,122,23125,2,4,L|217:123,1,120
+99,106,24500,1,4,0:0:0:0:
+99,106,24625,1,4,0:0:0:0:
+99,106,24750,2,4,L|194:107,1,80
+99,106,25125,1,4,0:0:0:0:
+99,106,25250,1,4,0:0:0:0:
+99,106,25375,2,4,L|194:107,1,80
+99,106,25750,1,4,0:0:0:0:
+99,106,25875,1,4,0:0:0:0:
+99,106,26000,2,4,L|194:107,1,80
+99,106,26375,1,4,0:0:0:0:
+99,106,26500,1,4,0:0:0:0:
+99,106,26625,2,4,L|194:107,1,80
+99,106,27000,1,4,0:0:0:0:
+99,106,27125,1,4,0:0:0:0:
+99,106,27250,2,4,L|194:107,1,80
+121,103,28500,1,4,0:0:0:0:
+121,103,28625,1,4,0:0:0:0:
+121,103,28750,1,4,0:0:0:0:
+121,103,28875,2,4,L|190:103,1,40
+121,103,29125,1,4,0:0:0:0:
+121,103,29250,1,4,0:0:0:0:
+121,103,29375,1,4,0:0:0:0:
+121,103,29500,2,4,L|190:103,1,40
+121,103,29750,1,4,0:0:0:0:
+121,103,29875,1,4,0:0:0:0:
+121,103,30000,1,4,0:0:0:0:
+121,103,30125,2,4,L|190:103,1,40
+121,103,30375,1,4,0:0:0:0:
+121,103,30500,1,4,0:0:0:0:
+121,103,30625,1,4,0:0:0:0:
+121,103,30750,2,4,L|190:103,1,40
+121,103,31000,1,4,0:0:0:0:
+121,103,31125,1,4,0:0:0:0:
+121,103,31250,1,4,0:0:0:0:
+121,103,31375,2,4,L|190:103,1,40
+121,103,32500,1,4,0:0:0:0:
+121,103,32625,1,6,0:0:0:0:
+121,103,32750,1,4,0:0:0:0:
+121,103,32875,2,4,L|190:103,1,40
+121,103,33125,1,4,0:0:0:0:
+121,103,33250,1,6,0:0:0:0:
+121,103,33375,1,4,0:0:0:0:
+121,103,33500,2,4,L|190:103,1,40
+121,103,33750,1,4,0:0:0:0:
+121,103,33875,1,6,0:0:0:0:
+121,103,34000,1,4,0:0:0:0:
+121,103,34125,2,4,L|190:103,1,40
+121,103,34375,1,4,0:0:0:0:
+121,103,34500,1,6,0:0:0:0:
+121,103,34625,1,4,0:0:0:0:
+121,103,34750,2,4,L|190:103,1,40
+121,103,35000,1,4,0:0:0:0:
+121,103,35125,1,6,0:0:0:0:
+121,103,35250,1,4,0:0:0:0:
+121,103,35375,2,4,L|190:103,1,40
+121,103,36500,1,4,0:0:0:0:
+121,103,36625,1,4,0:0:0:0:
+121,103,36750,1,6,0:0:0:0:
+121,103,36875,2,4,L|190:103,1,40
+121,103,37125,1,4,0:0:0:0:
+121,103,37250,1,4,0:0:0:0:
+121,103,37375,1,6,0:0:0:0:
+121,103,37500,2,4,L|190:103,1,40
+121,103,37750,1,4,0:0:0:0:
+121,103,37875,1,4,0:0:0:0:
+121,103,38000,1,6,0:0:0:0:
+121,103,38125,2,4,L|190:103,1,40
+121,103,38375,1,4,0:0:0:0:
+121,103,38500,1,4,0:0:0:0:
+121,103,38625,1,6,0:0:0:0:
+121,103,38750,2,4,L|190:103,1,40
+121,103,39000,1,4,0:0:0:0:
+121,103,39125,1,4,0:0:0:0:
+121,103,39250,1,6,0:0:0:0:
+121,103,39375,2,4,L|190:103,1,40
+107,106,40500,1,4,0:0:0:0:
+107,106,40625,1,4,0:0:0:0:
+107,106,40750,1,6,0:0:0:0:
+107,106,40875,1,6,0:0:0:0:
+46,112,41000,2,4,L|214:112,1,160
+107,106,41625,1,4,0:0:0:0:
+107,106,41750,1,4,0:0:0:0:
+107,106,41875,1,6,0:0:0:0:
+107,106,42000,1,6,0:0:0:0:
+46,112,42125,2,4,L|214:112,1,160
+107,106,42750,1,4,0:0:0:0:
+107,106,42875,1,4,0:0:0:0:
+107,106,43000,1,6,0:0:0:0:
+107,106,43125,1,6,0:0:0:0:
+46,112,43250,2,4,L|214:112,1,160
+107,106,43875,1,4,0:0:0:0:
+107,106,44000,1,4,0:0:0:0:
+107,106,44125,1,6,0:0:0:0:
+107,106,44250,1,6,0:0:0:0:
+46,112,44375,2,4,L|214:112,1,160
+107,106,45000,1,4,0:0:0:0:
+107,106,45125,1,4,0:0:0:0:
+107,106,45250,1,6,0:0:0:0:
+107,106,45375,1,6,0:0:0:0:
+46,112,45500,2,4,L|214:112,1,160
+256,192,47000,12,4,47500,0:0:0:0:
+256,192,47625,12,4,48000,0:0:0:0:
+256,192,48125,12,4,48500,0:0:0:0:
+256,192,48625,12,4,49000,0:0:0:0:
+256,192,50000,12,4,50500,0:0:0:0:
+183,143,50625,5,4,0:0:0:0:
+256,192,50750,12,4,51250,0:0:0:0:
+114,106,51375,5,4,0:0:0:0:
+256,192,51625,12,4,52125,0:0:0:0:
+154,143,52250,5,4,0:0:0:0:
+256,192,52375,12,4,52875,0:0:0:0:
+116,111,53000,5,4,0:0:0:0:
diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test.osu
new file mode 100644
index 0000000000..15326162ea
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test.osu
@@ -0,0 +1,285 @@
+osu file format v14
+
+[General]
+Mode: 1
+
+[Difficulty]
+CircleSize:4
+OverallDifficulty:7
+ApproachRate:8.3
+SliderMultiplier:1.6
+SliderTickRate:1
+
+[TimingPoints]
+500,500,4,2,1,50,1,0
+62500,-500,4,2,1,50,0,0
+71000,-100,4,2,1,50,0,0
+
+[HitObjects]
+// dd, spaced 1/4 beat apart
+142,122,0,5,0,0:0:0:0:
+142,122,125,1,0,0:0:0:0:
+142,122,250,1,0,0:0:0:0:
+142,122,375,1,0,0:0:0:0:
+142,122,500,1,0,0:0:0:0:
+142,122,625,1,0,0:0:0:0:
+142,122,750,1,0,0:0:0:0:
+142,122,875,1,0,0:0:0:0:
+142,122,1000,1,0,0:0:0:0:
+142,122,1125,1,0,0:0:0:0:
+142,122,1250,1,0,0:0:0:0:
+142,122,1375,1,0,0:0:0:0:
+142,122,1500,1,0,0:0:0:0:
+
+// kk, spaced 1/4 beat apart
+119,106,2500,1,2,0:0:0:0:
+119,106,2625,1,2,0:0:0:0:
+119,106,2750,1,2,0:0:0:0:
+119,106,2875,1,2,0:0:0:0:
+119,106,3000,1,2,0:0:0:0:
+119,106,3125,1,2,0:0:0:0:
+119,106,3250,1,2,0:0:0:0:
+119,106,3375,1,2,0:0:0:0:
+119,106,3500,1,2,0:0:0:0:
+119,106,3625,1,2,0:0:0:0:
+119,106,3750,1,2,0:0:0:0:
+119,106,3875,1,2,0:0:0:0:
+119,106,4000,1,2,0:0:0:0:
+
+// dk, spaced 1/4 beat apart
+136,90,5000,1,0,0:0:0:0:
+136,90,5125,1,2,0:0:0:0:
+136,90,5250,1,0,0:0:0:0:
+136,90,5375,1,2,0:0:0:0:
+136,90,5500,1,0,0:0:0:0:
+136,90,5625,1,2,0:0:0:0:
+136,90,5750,1,0,0:0:0:0:
+136,90,5875,1,2,0:0:0:0:
+136,90,6000,1,0,0:0:0:0:
+136,90,6125,1,2,0:0:0:0:
+136,90,6250,1,0,0:0:0:0:
+136,90,6375,1,2,0:0:0:0:
+136,90,6500,1,0,0:0:0:0:
+
+// ddkk, spaced 1/4 beat apart
+86,113,7500,1,0,0:0:0:0:
+86,113,7625,1,0,0:0:0:0:
+86,113,7750,1,2,0:0:0:0:
+86,113,7875,1,2,0:0:0:0:
+86,113,8000,1,0,0:0:0:0:
+86,113,8125,1,0,0:0:0:0:
+86,113,8250,1,2,0:0:0:0:
+86,113,8375,1,2,0:0:0:0:
+86,113,8500,1,0,0:0:0:0:
+86,113,8625,1,0,0:0:0:0:
+86,113,8750,1,2,0:0:0:0:
+86,113,8875,1,2,0:0:0:0:
+86,113,9000,1,0,0:0:0:0:
+
+// dddkkk, spaced 1/4 beat apart
+146,90,10000,1,0,0:0:0:0:
+146,90,10125,1,0,0:0:0:0:
+146,90,10250,1,0,0:0:0:0:
+146,90,10375,1,2,0:0:0:0:
+146,90,10500,1,2,0:0:0:0:
+146,90,10625,1,2,0:0:0:0:
+146,90,10750,1,0,0:0:0:0:
+146,90,10875,1,0,0:0:0:0:
+146,90,11000,1,0,0:0:0:0:
+146,90,11125,1,2,0:0:0:0:
+146,90,11250,1,2,0:0:0:0:
+146,90,11375,1,2,0:0:0:0:
+146,90,11500,1,0,0:0:0:0:
+146,90,11625,1,0,0:0:0:0:
+146,90,11750,1,0,0:0:0:0:
+146,90,11875,1,2,0:0:0:0:
+146,90,12000,1,2,0:0:0:0:
+146,90,12125,1,2,0:0:0:0:
+146,90,12250,1,0,0:0:0:0:
+146,90,12375,1,0,0:0:0:0:
+146,90,12500,1,0,0:0:0:0:
+
+// dddk, spaced 1/4 beat apart
+69,99,13500,1,0,0:0:0:0:
+69,99,13625,1,0,0:0:0:0:
+69,99,13750,1,0,0:0:0:0:
+69,99,13875,1,2,0:0:0:0:
+69,99,14000,1,0,0:0:0:0:
+69,99,14125,1,0,0:0:0:0:
+69,99,14250,1,0,0:0:0:0:
+69,99,14375,1,2,0:0:0:0:
+69,99,14500,1,0,0:0:0:0:
+69,99,14625,1,0,0:0:0:0:
+69,99,14750,1,0,0:0:0:0:
+69,99,14875,1,2,0:0:0:0:
+69,99,15000,1,0,0:0:0:0:
+69,99,15125,1,0,0:0:0:0:
+69,99,15250,1,0,0:0:0:0:
+69,99,15375,1,2,0:0:0:0:
+69,99,15500,1,0,0:0:0:0:
+
+// arbitrary pattern, spaced 1/4 beat apart
+83,89,16500,1,0,0:0:0:0:
+83,89,16625,1,2,0:0:0:0:
+83,89,16750,1,2,0:0:0:0:
+83,89,16875,1,0,0:0:0:0:
+83,89,17000,1,0,0:0:0:0:
+83,89,17125,1,0,0:0:0:0:
+83,89,17250,1,2,0:0:0:0:
+83,89,17375,1,2,0:0:0:0:
+83,89,17500,1,2,0:0:0:0:
+83,89,17625,1,2,0:0:0:0:
+83,89,17750,1,0,0:0:0:0:
+83,89,17875,1,0,0:0:0:0:
+83,89,18000,1,0,0:0:0:0:
+83,89,18125,1,0,0:0:0:0:
+83,89,18250,1,0,0:0:0:0:
+83,89,18375,1,2,0:0:0:0:
+83,89,18500,1,2,0:0:0:0:
+83,89,18625,1,2,0:0:0:0:
+83,89,18750,1,2,0:0:0:0:
+83,89,18875,1,0,0:0:0:0:
+83,89,19000,1,0,0:0:0:0:
+83,89,19125,1,0,0:0:0:0:
+83,89,19250,1,0,0:0:0:0:
+83,89,19375,1,2,0:0:0:0:
+83,89,19500,1,2,0:0:0:0:
+83,89,19625,1,0,0:0:0:0:
+
+// d-slider pattern, spaced 1/4 beat apart
+84,122,20500,1,0,0:0:0:0:
+84,122,20625,2,0,L|217:123,1,120
+84,122,21125,1,0,0:0:0:0:
+84,122,21250,2,0,L|217:123,1,120
+84,122,21750,1,0,0:0:0:0:
+84,122,21875,2,0,L|217:123,1,120
+84,122,22375,1,0,0:0:0:0:
+84,122,22500,2,0,L|217:123,1,120
+84,122,23000,1,0,0:0:0:0:
+84,122,23125,2,0,L|217:123,1,120
+
+// dd-slider pattern, spaced 1/4 beat apart
+99,106,24500,1,0,0:0:0:0:
+99,106,24625,1,0,0:0:0:0:
+99,106,24750,2,0,L|194:107,1,80
+99,106,25125,1,0,0:0:0:0:
+99,106,25250,1,0,0:0:0:0:
+99,106,25375,2,0,L|194:107,1,80
+99,106,25750,1,0,0:0:0:0:
+99,106,25875,1,0,0:0:0:0:
+99,106,26000,2,0,L|194:107,1,80
+99,106,26375,1,0,0:0:0:0:
+99,106,26500,1,0,0:0:0:0:
+99,106,26625,2,0,L|194:107,1,80
+99,106,27000,1,0,0:0:0:0:
+99,106,27125,1,0,0:0:0:0:
+99,106,27250,2,0,L|194:107,1,80
+
+// ddd-slider pattern, spaced 1/4 beat apart
+121,103,28500,1,0,0:0:0:0:
+121,103,28625,1,0,0:0:0:0:
+121,103,28750,1,0,0:0:0:0:
+121,103,28875,2,0,L|190:103,1,40
+121,103,29125,1,0,0:0:0:0:
+121,103,29250,1,0,0:0:0:0:
+121,103,29375,1,0,0:0:0:0:
+121,103,29500,2,0,L|190:103,1,40
+121,103,29750,1,0,0:0:0:0:
+121,103,29875,1,0,0:0:0:0:
+121,103,30000,1,0,0:0:0:0:
+121,103,30125,2,0,L|190:103,1,40
+121,103,30375,1,0,0:0:0:0:
+121,103,30500,1,0,0:0:0:0:
+121,103,30625,1,0,0:0:0:0:
+121,103,30750,2,0,L|190:103,1,40
+121,103,31000,1,0,0:0:0:0:
+121,103,31125,1,0,0:0:0:0:
+121,103,31250,1,0,0:0:0:0:
+121,103,31375,2,0,L|190:103,1,40
+
+// dkd-slider pattern, spaced 1/4 beat apart
+121,103,32500,1,0,0:0:0:0:
+121,103,32625,1,2,0:0:0:0:
+121,103,32750,1,0,0:0:0:0:
+121,103,32875,2,0,L|190:103,1,40
+121,103,33125,1,0,0:0:0:0:
+121,103,33250,1,2,0:0:0:0:
+121,103,33375,1,0,0:0:0:0:
+121,103,33500,2,0,L|190:103,1,40
+121,103,33750,1,0,0:0:0:0:
+121,103,33875,1,2,0:0:0:0:
+121,103,34000,1,0,0:0:0:0:
+121,103,34125,2,0,L|190:103,1,40
+121,103,34375,1,0,0:0:0:0:
+121,103,34500,1,2,0:0:0:0:
+121,103,34625,1,0,0:0:0:0:
+121,103,34750,2,0,L|190:103,1,40
+121,103,35000,1,0,0:0:0:0:
+121,103,35125,1,2,0:0:0:0:
+121,103,35250,1,0,0:0:0:0:
+121,103,35375,2,0,L|190:103,1,40
+
+//ddk-slider pattern, spaced 1/4 beat apart
+121,103,36500,1,0,0:0:0:0:
+121,103,36625,1,0,0:0:0:0:
+121,103,36750,1,2,0:0:0:0:
+121,103,36875,2,0,L|190:103,1,40
+121,103,37125,1,0,0:0:0:0:
+121,103,37250,1,0,0:0:0:0:
+121,103,37375,1,2,0:0:0:0:
+121,103,37500,2,0,L|190:103,1,40
+121,103,37750,1,0,0:0:0:0:
+121,103,37875,1,0,0:0:0:0:
+121,103,38000,1,2,0:0:0:0:
+121,103,38125,2,0,L|190:103,1,40
+121,103,38375,1,0,0:0:0:0:
+121,103,38500,1,0,0:0:0:0:
+121,103,38625,1,2,0:0:0:0:
+121,103,38750,2,0,L|190:103,1,40
+121,103,39000,1,0,0:0:0:0:
+121,103,39125,1,0,0:0:0:0:
+121,103,39250,1,2,0:0:0:0:
+121,103,39375,2,0,L|190:103,1,40
+
+//ddkk-slider pattern, spaced 1/4 beat apart
+107,106,40500,1,0,0:0:0:0:
+107,106,40625,1,0,0:0:0:0:
+107,106,40750,1,2,0:0:0:0:
+107,106,40875,1,2,0:0:0:0:
+46,112,41000,2,0,L|214:112,1,160
+107,106,41625,1,0,0:0:0:0:
+107,106,41750,1,0,0:0:0:0:
+107,106,41875,1,2,0:0:0:0:
+107,106,42000,1,2,0:0:0:0:
+46,112,42125,2,0,L|214:112,1,160
+107,106,42750,1,0,0:0:0:0:
+107,106,42875,1,0,0:0:0:0:
+107,106,43000,1,2,0:0:0:0:
+107,106,43125,1,2,0:0:0:0:
+46,112,43250,2,0,L|214:112,1,160
+107,106,43875,1,0,0:0:0:0:
+107,106,44000,1,0,0:0:0:0:
+107,106,44125,1,2,0:0:0:0:
+107,106,44250,1,2,0:0:0:0:
+46,112,44375,2,0,L|214:112,1,160
+107,106,45000,1,0,0:0:0:0:
+107,106,45125,1,0,0:0:0:0:
+107,106,45250,1,2,0:0:0:0:
+107,106,45375,1,2,0:0:0:0:
+46,112,45500,2,0,L|214:112,1,160
+
+// spinner-spinner pattern, spaced 1/4 beat apart
+256,192,47000,12,0,47500,0:0:0:0:
+256,192,47625,12,0,48000,0:0:0:0:
+256,192,48125,12,0,48500,0:0:0:0:
+256,192,48625,12,0,49000,0:0:0:0:
+
+// spinner-d pattern, spaced 1/4 beat apart
+256,192,50000,12,0,50500,0:0:0:0:
+183,143,50625,5,0,0:0:0:0:
+256,192,50750,12,0,51250,0:0:0:0:
+114,106,51375,5,0,0:0:0:0:
+256,192,51625,12,0,52125,0:0:0:0:
+154,143,52250,5,0,0:0:0:0:
+256,192,52375,12,0,52875,0:0:0:0:
+116,111,53000,5,0,0:0:0:0:
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index f7638a8122..73cd9ba821 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -67,6 +67,6 @@ namespace osu.Game.Rulesets.Taiko.Scoring
Health.Value = 0;
}
- protected override HitWindows CreateHitWindows() => new TaikoHitWindows();
+ public override HitWindows CreateHitWindows() => new TaikoHitWindows();
}
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
index 2aae0b23b7..058bec5111 100644
--- a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
@@ -19,10 +19,13 @@ namespace osu.Game.Rulesets.Taiko
{
[Description("Left (rim)")]
LeftRim,
+
[Description("Left (centre)")]
LeftCentre,
+
[Description("Right (centre)")]
RightCentre,
+
[Description("Right (rim)")]
RightRim
}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index 8dc9a2ca37..cb527adb98 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -225,7 +225,7 @@ namespace osu.Game.Rulesets.Taiko.UI
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!DisplayJudgements)
+ if (!DisplayJudgements.Value)
return;
if (!judgedObject.DisplayResult)
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index 002d6a7e8d..656ebcc7c2 100644
--- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
+++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
@@ -10,4 +10,4 @@
-
\ No newline at end of file
+
diff --git a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
index 636bedac13..ea5ab699f3 100644
--- a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
+++ b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
@@ -51,10 +51,6 @@
{F167E17A-7DE6-4AF5-B920-A5112296C695}
osu.Game.Rulesets.Taiko
-
- {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}
- osu.Game.Resources
-
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 1221212f94..02dff6993d 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapVersion()
{
- using (var resStream = Resource.OpenResource("beatmap-version.osu"))
+ using (var resStream = TestResources.OpenResource("beatmap-version.osu"))
using (var stream = new StreamReader(resStream))
{
var decoder = Decoder.GetDecoder(stream);
@@ -47,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapGeneral()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapEditor()
{
var decoder = new LegacyBeatmapDecoder();
- using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmapInfo = decoder.Decode(stream).BeatmapInfo;
@@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapMetadata()
{
var decoder = new LegacyBeatmapDecoder();
- using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@@ -119,7 +119,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapDifficulty()
{
var decoder = new LegacyBeatmapDecoder();
- using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty;
@@ -137,7 +137,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapEvents()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@@ -155,7 +155,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapTimingPoints()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@@ -190,7 +190,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapColours()
{
var decoder = new LegacySkinDecoder();
- using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var comboColors = decoder.Decode(stream).ComboColours;
@@ -215,7 +215,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapComboOffsetsOsu()
{
var decoder = new LegacyBeatmapDecoder();
- using (var resStream = Resource.OpenResource("hitobject-combo-offset.osu"))
+ using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@@ -237,7 +237,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapComboOffsetsCatch()
{
var decoder = new LegacyBeatmapDecoder();
- using (var resStream = Resource.OpenResource("hitobject-combo-offset.osu"))
+ using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu"))
using (var stream = new StreamReader(resStream))
{
var beatmap = decoder.Decode(stream);
@@ -259,7 +259,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapHitObjects()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@@ -286,7 +286,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeControlPointCustomSampleBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("controlpoint-custom-samplebank.osu"))
+ using (var resStream = TestResources.OpenResource("controlpoint-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@@ -307,7 +307,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeHitObjectCustomSampleBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("hitobject-custom-samplebank.osu"))
+ using (var resStream = TestResources.OpenResource("hitobject-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@@ -324,7 +324,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeHitObjectFileSamples()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("hitobject-file-samples.osu"))
+ using (var resStream = TestResources.OpenResource("hitobject-file-samples.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@@ -333,6 +333,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("hit_2.wav", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
Assert.AreEqual("hit_1.wav", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
+ Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
}
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
@@ -342,7 +343,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeSliderSamples()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("slider-samples.osu"))
+ using (var resStream = TestResources.OpenResource("slider-samples.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
@@ -385,7 +386,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeHitObjectNullAdditionBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
- using (var resStream = Resource.OpenResource("hitobject-no-addition-bank.osu"))
+ using (var resStream = TestResources.OpenResource("hitobject-no-addition-bank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
index 5049349999..136d1de930 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeStoryboardEvents()
{
var decoder = new LegacyStoryboardDecoder();
- using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
+ using (var resStream = TestResources.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
using (var stream = new StreamReader(resStream))
{
var storyboard = decoder.Decode(stream);
@@ -91,7 +91,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeVariableWithSuffix()
{
var decoder = new LegacyStoryboardDecoder();
- using (var resStream = Resource.OpenResource("variable-with-suffix.osb"))
+ using (var resStream = TestResources.OpenResource("variable-with-suffix.osb"))
using (var stream = new StreamReader(resStream))
{
var storyboard = decoder.Decode(stream);
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 44291a6805..d5ab0e43d1 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -146,7 +146,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
/// The after being decoded by an .
private Beatmap decode(string filename, out Beatmap jsonDecoded)
{
- using (var stream = Resource.OpenResource(filename))
+ using (var stream = TestResources.OpenResource(filename))
using (var sr = new StreamReader(stream))
{
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index a159659d35..5b8bdd8a51 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -12,6 +12,7 @@ using osu.Framework.Platform;
using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
+using osu.Game.Tests.Resources;
using SharpCompress.Archives.Zip;
namespace osu.Game.Tests.Beatmaps.IO
@@ -19,8 +20,6 @@ namespace osu.Game.Tests.Beatmaps.IO
[TestFixture]
public class ImportBeatmapTest
{
- public const string TEST_OSZ_PATH = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
-
[Test]
public void TestImportWhenClosed()
{
@@ -102,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.IO
int fireCount = 0;
// ReSharper disable once AccessToModifiedClosure
- manager.ItemAdded += (_, __, ___) => fireCount++;
+ manager.ItemAdded += (_, __) => fireCount++;
manager.ItemRemoved += _ => fireCount++;
var imported = LoadOszIntoOsu(osu);
@@ -114,7 +113,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.AreEqual(0, fireCount -= 2);
- var breakTemp = createTemporaryBeatmap();
+ var breakTemp = TestResources.GetTestBeatmapForImport();
MemoryStream brokenOsu = new MemoryStream(new byte[] { 1, 3, 3, 7 });
MemoryStream brokenOsz = new MemoryStream(File.ReadAllBytes(breakTemp));
@@ -223,7 +222,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var osu = loadOsu(host);
- var temp = createTemporaryBeatmap();
+ var temp = TestResources.GetTestBeatmapForImport();
var importer = new ArchiveImportIPCChannel(client);
if (!importer.ImportAsync(temp).Wait(10000))
@@ -248,7 +247,7 @@ namespace osu.Game.Tests.Beatmaps.IO
try
{
var osu = loadOsu(host);
- var temp = createTemporaryBeatmap();
+ var temp = TestResources.GetTestBeatmapForImport();
using (File.OpenRead(temp))
osu.Dependencies.Get().Import(temp);
ensureLoaded(osu);
@@ -262,17 +261,9 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
- private static string createTemporaryBeatmap()
- {
- var temp = Path.GetTempFileName() + ".osz";
- File.Copy(TEST_OSZ_PATH, temp, true);
- Assert.IsTrue(File.Exists(temp));
- return temp;
- }
-
public static BeatmapSetInfo LoadOszIntoOsu(OsuGameBase osu, string path = null)
{
- var temp = path ?? createTemporaryBeatmap();
+ var temp = path ?? TestResources.GetTestBeatmapForImport();
var manager = osu.Dependencies.Get();
diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
index aa95e7390f..17197aff27 100644
--- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public void TestReadBeatmaps()
{
- using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
+ using (var osz = TestResources.GetTestBeatmapStream())
{
var reader = new ZipArchiveReader(osz);
string[] expected =
@@ -44,7 +44,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public void TestReadMetadata()
{
- using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
+ using (var osz = TestResources.GetTestBeatmapStream())
{
var reader = new ZipArchiveReader(osz);
@@ -72,7 +72,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public void TestReadFile()
{
- using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
+ using (var osz = TestResources.GetTestBeatmapStream())
{
var reader = new ZipArchiveReader(osz);
using (var stream = new StreamReader(
diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs
index 795c2244a2..760a033aff 100644
--- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs
+++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs
@@ -2,9 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Tests.NonVisual
@@ -15,7 +18,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestNoMods()
{
- var combinations = new TestDifficultyCalculator().CreateDifficultyAdjustmentModCombinations();
+ var combinations = new TestLegacyDifficultyCalculator().CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(1, combinations.Length);
Assert.IsTrue(combinations[0] is ModNoMod);
@@ -24,7 +27,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestSingleMod()
{
- var combinations = new TestDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations();
+ var combinations = new TestLegacyDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(2, combinations.Length);
Assert.IsTrue(combinations[0] is ModNoMod);
@@ -34,7 +37,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestDoubleMod()
{
- var combinations = new TestDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations();
+ var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(4, combinations.Length);
Assert.IsTrue(combinations[0] is ModNoMod);
@@ -49,7 +52,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestIncompatibleMods()
{
- var combinations = new TestDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations();
+ var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(3, combinations.Length);
Assert.IsTrue(combinations[0] is ModNoMod);
@@ -60,7 +63,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestDoubleIncompatibleMods()
{
- var combinations = new TestDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations();
+ var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(8, combinations.Length);
Assert.IsTrue(combinations[0] is ModNoMod);
@@ -83,7 +86,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestIncompatibleThroughBaseType()
{
- var combinations = new TestDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations();
+ var combinations = new TestLegacyDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations();
Assert.AreEqual(3, combinations.Length);
Assert.IsTrue(combinations[0] is ModNoMod);
@@ -136,9 +139,9 @@ namespace osu.Game.Tests.NonVisual
public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) };
}
- private class TestDifficultyCalculator : DifficultyCalculator
+ private class TestLegacyDifficultyCalculator : DifficultyCalculator
{
- public TestDifficultyCalculator(params Mod[] mods)
+ public TestLegacyDifficultyCalculator(params Mod[] mods)
: base(null, null)
{
DifficultyAdjustmentMods = mods;
@@ -146,7 +149,20 @@ namespace osu.Game.Tests.NonVisual
protected override Mod[] DifficultyAdjustmentMods { get; }
- protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => throw new NotImplementedException();
+ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Skill[] CreateSkills(IBeatmap beatmap)
+ {
+ throw new NotImplementedException();
+ }
}
}
}
diff --git a/osu.Game.Tests/NonVisual/LimitedCapacityStackTest.cs b/osu.Game.Tests/NonVisual/LimitedCapacityStackTest.cs
new file mode 100644
index 0000000000..1c78b63499
--- /dev/null
+++ b/osu.Game.Tests/NonVisual/LimitedCapacityStackTest.cs
@@ -0,0 +1,115 @@
+// 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 NUnit.Framework;
+using osu.Game.Rulesets.Difficulty.Utils;
+
+namespace osu.Game.Tests.NonVisual
+{
+ [TestFixture]
+ public class LimitedCapacityStackTest
+ {
+ private const int capacity = 3;
+
+ private LimitedCapacityStack stack;
+
+ [SetUp]
+ public void Setup()
+ {
+ stack = new LimitedCapacityStack(capacity);
+ }
+
+ [Test]
+ public void TestEmptyStack()
+ {
+ Assert.AreEqual(0, stack.Count);
+
+ Assert.Throws(() =>
+ {
+ int unused = stack[0];
+ });
+
+ int count = 0;
+ foreach (var unused in stack)
+ count++;
+
+ Assert.AreEqual(0, count);
+ }
+
+ [TestCase(1)]
+ [TestCase(2)]
+ [TestCase(3)]
+ public void TestInRangeElements(int count)
+ {
+ // e.g. 0 -> 1 -> 2
+ for (int i = 0; i < count; i++)
+ stack.Push(i);
+
+ Assert.AreEqual(count, stack.Count);
+
+ // e.g. 2 -> 1 -> 0 (reverse order)
+ for (int i = 0; i < stack.Count; i++)
+ Assert.AreEqual(count - 1 - i, stack[i]);
+
+ // e.g. indices 3, 4, 5, 6 (out of range)
+ for (int i = stack.Count; i < stack.Count + capacity; i++)
+ {
+ Assert.Throws(() =>
+ {
+ int unused = stack[i];
+ });
+ }
+ }
+
+ [TestCase(4)]
+ [TestCase(5)]
+ [TestCase(6)]
+ public void TestOverflowElements(int count)
+ {
+ // e.g. 0 -> 1 -> 2 -> 3
+ for (int i = 0; i < count; i++)
+ stack.Push(i);
+
+ Assert.AreEqual(capacity, stack.Count);
+
+ // e.g. 3 -> 2 -> 1 (reverse order)
+ for (int i = 0; i < stack.Count; i++)
+ Assert.AreEqual(count - 1 - i, stack[i]);
+
+ // e.g. indices 3, 4, 5, 6 (out of range)
+ for (int i = stack.Count; i < stack.Count + capacity; i++)
+ {
+ Assert.Throws(() =>
+ {
+ int unused = stack[i];
+ });
+ }
+ }
+
+ [TestCase(1)]
+ [TestCase(2)]
+ [TestCase(3)]
+ [TestCase(4)]
+ [TestCase(5)]
+ [TestCase(6)]
+ public void TestEnumerator(int count)
+ {
+ // e.g. 0 -> 1 -> 2 -> 3
+ for (int i = 0; i < count; i++)
+ stack.Push(i);
+
+ int enumeratorCount = 0;
+ int expectedValue = count - 1;
+
+ foreach (var item in stack)
+ {
+ Assert.AreEqual(expectedValue, item);
+ enumeratorCount++;
+ expectedValue--;
+ }
+
+ Assert.AreEqual(stack.Count, enumeratorCount);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Resources/Resource.cs b/osu.Game.Tests/Resources/Resource.cs
deleted file mode 100644
index 5405436422..0000000000
--- a/osu.Game.Tests/Resources/Resource.cs
+++ /dev/null
@@ -1,20 +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.IO;
-using System.Reflection;
-
-namespace osu.Game.Tests.Resources
-{
- public static class Resource
- {
- public static Stream OpenResource(string name)
- {
- var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path));
-
- return Assembly.GetExecutingAssembly().GetManifestResourceStream($@"osu.Game.Tests.Resources.{name}") ??
- Assembly.LoadFrom(Path.Combine(localPath, @"osu.Game.Resources.dll")).GetManifestResourceStream($@"osu.Game.Resources.{name}");
- }
- }
-}
diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs
new file mode 100644
index 0000000000..9cb85a63bf
--- /dev/null
+++ b/osu.Game.Tests/Resources/TestResources.cs
@@ -0,0 +1,28 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.IO;
+using NUnit.Framework;
+using osu.Framework.IO.Stores;
+
+namespace osu.Game.Tests.Resources
+{
+ public static class TestResources
+ {
+ public static Stream OpenResource(string name) => new DllResourceStore("osu.Game.Tests.dll").GetStream($"Resources/{name}");
+
+ public static Stream GetTestBeatmapStream() => new DllResourceStore("osu.Game.Resources.dll").GetStream("Beatmaps/241526 Soleily - Renatus.osz");
+
+ public static string GetTestBeatmapForImport()
+ {
+ var temp = Path.GetTempFileName() + ".osz";
+
+ using (var stream = GetTestBeatmapStream())
+ using (var newFile = File.Create(temp))
+ stream.CopyTo(newFile);
+
+ Assert.IsTrue(File.Exists(temp));
+ return temp;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Resources/hitobject-file-samples.osu b/osu.Game.Tests/Resources/hitobject-file-samples.osu
index 588672e2d9..7c69f259b8 100644
--- a/osu.Game.Tests/Resources/hitobject-file-samples.osu
+++ b/osu.Game.Tests/Resources/hitobject-file-samples.osu
@@ -13,4 +13,4 @@ SampleSet: Normal
255,193,2170,1,0,0:0:0:0:hit_1.wav
256,191,2638,5,0,0:0:0:0:hit_2.wav
255,193,3107,1,0,0:0:0:0:
-256,191,3576,1,0,0:0:0:0:hit_1.wav
+256,191,3576,1,0,0:0:0:70:hit_1.wav
diff --git a/osu.Game.Tests/Resources/skin-empty.ini b/osu.Game.Tests/Resources/skin-empty.ini
new file mode 100644
index 0000000000..b6c319fe3c
--- /dev/null
+++ b/osu.Game.Tests/Resources/skin-empty.ini
@@ -0,0 +1,2 @@
+[General]
+Name: test skin
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/skin.ini b/osu.Game.Tests/Resources/skin.ini
new file mode 100644
index 0000000000..0e5737b4ea
--- /dev/null
+++ b/osu.Game.Tests/Resources/skin.ini
@@ -0,0 +1,8 @@
+[General]
+Name: test skin
+
+[Colours]
+Combo1 : 142,199,255
+Combo2 : 255,128,128
+Combo3 : 128,255,255
+Combo7 : 100,100,100,100
\ No newline at end of file
diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
index f5ebe313dd..e39f18c3cd 100644
--- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
+++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -16,14 +15,13 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
+using osu.Game.Tests.Resources;
using osu.Game.Users;
namespace osu.Game.Tests.Scores.IO
{
public class ImportScoreTest
{
- public const string TEST_OSZ_PATH = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
-
[Test]
public void TestBasicImport()
{
@@ -132,21 +130,13 @@ namespace osu.Game.Tests.Scores.IO
return scoreManager.GetAllUsableScores().First();
}
- private string createTemporaryBeatmap()
- {
- var temp = Path.GetTempFileName() + ".osz";
- File.Copy(TEST_OSZ_PATH, temp, true);
- Assert.IsTrue(File.Exists(temp));
- return temp;
- }
-
private OsuGameBase loadOsu(GameHost host)
{
var osu = new OsuGameBase();
Task.Run(() => host.Run(osu));
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
- var beatmapFile = createTemporaryBeatmap();
+ var beatmapFile = TestResources.GetTestBeatmapForImport();
var beatmapManager = osu.Dependencies.Get();
beatmapManager.Import(beatmapFile);
diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
new file mode 100644
index 0000000000..2a97519e21
--- /dev/null
+++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
@@ -0,0 +1,44 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.IO;
+using NUnit.Framework;
+using osu.Game.Skinning;
+using osu.Game.Tests.Resources;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Skins
+{
+ [TestFixture]
+ public class LegacySkinDecoderTest
+ {
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TestDecodeSkinColours(bool hasColours)
+ {
+ var decoder = new LegacySkinDecoder();
+ using (var resStream = TestResources.OpenResource(hasColours ? "skin.ini" : "skin-empty.ini"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var comboColors = decoder.Decode(stream).ComboColours;
+
+ List expectedColors;
+ if (hasColours)
+ expectedColors = new List
+ {
+ new Color4(142, 199, 255, 255),
+ new Color4(255, 128, 128, 255),
+ new Color4(128, 255, 255, 255),
+ new Color4(100, 100, 100, 100),
+ };
+ else
+ expectedColors = new DefaultSkin().Configuration.ComboColours;
+
+ Assert.AreEqual(expectedColors.Count, comboColors.Count);
+ for (int i = 0; i < expectedColors.Count; i++)
+ Assert.AreEqual(expectedColors[i], comboColors[i]);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseAutoplay.cs b/osu.Game.Tests/Visual/TestCaseAutoplay.cs
index 3f3d62377e..61339a6af8 100644
--- a/osu.Game.Tests/Visual/TestCaseAutoplay.cs
+++ b/osu.Game.Tests/Visual/TestCaseAutoplay.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual
protected override void AddCheckSteps(Func player)
{
base.AddCheckSteps(player);
- AddUntilStep(() => ((ScoreAccessiblePlayer)player()).ScoreProcessor.TotalScore > 0, "score above zero");
+ AddUntilStep(() => ((ScoreAccessiblePlayer)player()).ScoreProcessor.TotalScore.Value > 0, "score above zero");
AddUntilStep(() => ((ScoreAccessiblePlayer)player()).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0), "key counter counted keys");
}
diff --git a/osu.Game.Tests/Visual/TestCaseBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/TestCaseBackgroundScreenBeatmap.cs
new file mode 100644
index 0000000000..5484824c5b
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseBackgroundScreenBeatmap.cs
@@ -0,0 +1,414 @@
+// 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 System.Threading;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input.Events;
+using osu.Framework.Input.States;
+using osu.Framework.Platform;
+using osu.Framework.Screens;
+using osu.Game.Beatmaps;
+using osu.Game.Configuration;
+using osu.Game.Database;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Scoring;
+using osu.Game.Screens;
+using osu.Game.Screens.Backgrounds;
+using osu.Game.Screens.Play;
+using osu.Game.Screens.Play.PlayerSettings;
+using osu.Game.Screens.Select;
+using osu.Game.Tests.Resources;
+using osu.Game.Users;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseBackgroundScreenBeatmap : ManualInputManagerTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ScreenWithBeatmapBackground),
+ typeof(PlayerLoader),
+ typeof(Player),
+ typeof(UserDimContainer),
+ typeof(OsuScreen)
+ };
+
+ private DummySongSelect songSelect;
+ private DimAccessiblePlayerLoader playerLoader;
+ private DimAccessiblePlayer player;
+ private DatabaseContextFactory factory;
+ private BeatmapManager manager;
+ private RulesetStore rulesets;
+
+ private ScreenStackCacheContainer screenStackContainer;
+
+ [BackgroundDependencyLoader]
+ private void load(GameHost host)
+ {
+ factory = new DatabaseContextFactory(LocalStorage);
+ factory.ResetDatabase();
+
+ using (var usage = factory.Get())
+ usage.Migrate();
+
+ factory.ResetDatabase();
+
+ using (var usage = factory.Get())
+ usage.Migrate();
+
+ Dependencies.Cache(rulesets = new RulesetStore(factory));
+ Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, host, Beatmap.Default));
+ Dependencies.Cache(new OsuConfigManager(LocalStorage));
+
+ Beatmap.SetDefault();
+ }
+
+ [SetUp]
+ public virtual void SetUp()
+ {
+ Schedule(() =>
+ {
+ manager.Delete(manager.GetAllUsableBeatmapSets());
+ var temp = TestResources.GetTestBeatmapForImport();
+ manager.Import(temp);
+ Child = screenStackContainer = new ScreenStackCacheContainer { RelativeSizeAxes = Axes.Both };
+ screenStackContainer.ScreenStack.Push(songSelect = new DummySongSelect());
+ });
+ }
+
+ ///
+ /// Check if properly triggers background dim previews when a user hovers over the visual settings panel.
+ ///
+ [Test]
+ public void PlayerLoaderSettingsHoverTest()
+ {
+ setupUserSettings();
+ AddStep("Start player loader", () => songSelect.Push(playerLoader = new DimAccessiblePlayerLoader(player = new DimAccessiblePlayer())));
+ AddUntilStep(() => playerLoader?.IsLoaded ?? false, "Wait for Player Loader to load");
+ AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
+ AddStep("Trigger background preview", () =>
+ {
+ InputManager.MoveMouseTo(playerLoader.ScreenPos);
+ InputManager.MoveMouseTo(playerLoader.VisualSettingsPos);
+ });
+ waitForDim();
+ AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
+ AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
+ waitForDim();
+ AddAssert("Screen is undimmed", () => songSelect.IsBackgroundUndimmed());
+ }
+
+ ///
+ /// In the case of a user triggering the dim preview the instant player gets loaded, then moving the cursor off of the visual settings:
+ /// The OnHover of PlayerLoader will trigger, which could potentially trigger an undim unless checked for in PlayerLoader.
+ /// We need to check that in this scenario, the dim is still properly applied after entering player.
+ ///
+ [Test]
+ public void PlayerLoaderTransitionTest()
+ {
+ performFullSetup();
+ AddStep("Trigger hover event", () => playerLoader.TriggerOnHover());
+ AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
+ waitForDim();
+ AddAssert("Screen is dimmed and unblurred", () => songSelect.IsBackgroundDimmed() && songSelect.IsBackgroundUnblurred());
+ }
+
+ ///
+ /// Make sure the background is fully invisible (Alpha == 0) when the background should be disabled by the storyboard.
+ ///
+ [Test]
+ public void StoryboardBackgroundVisibilityTest()
+ {
+ performFullSetup();
+ createFakeStoryboard();
+ AddStep("Storyboard Enabled", () =>
+ {
+ player.ReplacesBackground.Value = true;
+ player.StoryboardEnabled.Value = true;
+ });
+ waitForDim();
+ AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible());
+ AddStep("Storyboard Disabled", () =>
+ {
+ player.ReplacesBackground.Value = false;
+ player.StoryboardEnabled.Value = false;
+ });
+ waitForDim();
+ AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && player.IsStoryboardInvisible());
+ }
+
+ ///
+ /// When exiting player, the screen that it suspends/exits to needs to have a fully visible (Alpha == 1) background.
+ ///
+ [Test]
+ public void StoryboardTransitionTest()
+ {
+ performFullSetup();
+ createFakeStoryboard();
+ AddStep("Exit to song select", () => player.Exit());
+ waitForDim();
+ AddAssert("Background is visible", () => songSelect.IsBackgroundVisible());
+ }
+
+ ///
+ /// Check if the is properly accepting user dim changes at all.
+ ///
+ [Test]
+ public void DisableUserDimTest()
+ {
+ performFullSetup();
+ waitForDim();
+ AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
+ AddStep("EnableUserDim disabled", () => songSelect.DimEnabled.Value = false);
+ waitForDim();
+ AddAssert("Screen is undimmed", () => songSelect.IsBackgroundUndimmed());
+ AddStep("EnableUserDim enabled", () => songSelect.DimEnabled.Value = true);
+ waitForDim();
+ AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
+ }
+
+ ///
+ /// Check if the fade container retains dim when pausing
+ ///
+ [Test]
+ public void PauseTest()
+ {
+ performFullSetup(true);
+ AddStep("Pause", () => player.CurrentPausableGameplayContainer.Pause());
+ waitForDim();
+ AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
+ AddStep("Unpause", () => player.CurrentPausableGameplayContainer.Resume());
+ waitForDim();
+ AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
+ }
+
+ ///
+ /// Check if the fade container removes user dim when suspending for
+ ///
+ [Test]
+ public void TransitionTest()
+ {
+ performFullSetup();
+ AddStep("Transition to Results", () => player.Push(new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } })));
+ waitForDim();
+ AddAssert("Screen is undimmed and is original background", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent());
+ }
+
+ ///
+ /// Check if background gets undimmed when leaving for
+ ///
+ [Test]
+ public void TransitionOutTest()
+ {
+ performFullSetup();
+ AddStep("Exit to song select", () => player.Exit());
+ waitForDim();
+ AddAssert("Screen is undimmed", () => songSelect.IsBackgroundUndimmed());
+ }
+
+ private void waitForDim() => AddWaitStep(5, "Wait for dim");
+
+ private void createFakeStoryboard() => AddStep("Create storyboard", () =>
+ {
+ player.StoryboardEnabled.Value = false;
+ player.ReplacesBackground.Value = false;
+ player.CurrentStoryboardContainer.Add(new SpriteText
+ {
+ Size = new Vector2(250, 50),
+ Alpha = 1,
+ Colour = Color4.Tomato,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = "THIS IS A STORYBOARD",
+ });
+ });
+
+ private void performFullSetup(bool allowPause = false)
+ {
+ setupUserSettings();
+
+ AddStep("Start player loader", () =>
+ {
+ songSelect.Push(playerLoader = new DimAccessiblePlayerLoader(player = new DimAccessiblePlayer
+ {
+ AllowPause = allowPause,
+ Ready = true,
+ }));
+ });
+ AddUntilStep(() => playerLoader.IsLoaded, "Wait for Player Loader to load");
+ AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
+ AddUntilStep(() => player.IsLoaded, "Wait for player to load");
+ }
+
+ private void setupUserSettings()
+ {
+ AddUntilStep(() => songSelect.Carousel.SelectedBeatmap != null, "Song select has selection");
+ AddStep("Set default user settings", () =>
+ {
+ Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { new OsuModNoFail() });
+ songSelect.DimLevel.Value = 0.7f;
+ songSelect.BlurLevel.Value = 0.0f;
+ });
+ }
+
+ private class DummySongSelect : PlaySongSelect
+ {
+ protected override BackgroundScreen CreateBackground()
+ {
+ FadeAccessibleBackground background = new FadeAccessibleBackground(Beatmap.Value);
+ DimEnabled.BindTo(background.EnableUserDim);
+ return background;
+ }
+
+ public readonly Bindable DimEnabled = new Bindable();
+ public readonly Bindable DimLevel = new Bindable();
+ public readonly Bindable BlurLevel = new Bindable();
+
+ public new BeatmapCarousel Carousel => base.Carousel;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config)
+ {
+ config.BindWith(OsuSetting.DimLevel, DimLevel);
+ config.BindWith(OsuSetting.BlurLevel, BlurLevel);
+ }
+
+ public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1 - (float)DimLevel.Value);
+
+ public bool IsBackgroundUnblurred() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0);
+
+ public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White;
+
+ public bool IsBackgroundInvisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 0;
+
+ public bool IsBackgroundVisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 1;
+
+ ///
+ /// Make sure every time a screen gets pushed, the background doesn't get replaced
+ ///
+ /// Whether or not the original background (The one created in DummySongSelect) is still the current background
+ public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen();
+ }
+
+ private class FadeAccessibleResults : SoloResults
+ {
+ public FadeAccessibleResults(ScoreInfo score)
+ : base(score)
+ {
+ }
+
+ protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
+ }
+
+ private class DimAccessiblePlayer : Player
+ {
+ protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
+
+ protected override UserDimContainer CreateStoryboardContainer()
+ {
+ return new TestUserDimContainer(true)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 1,
+ EnableUserDim = { Value = true }
+ };
+ }
+
+ public PausableGameplayContainer CurrentPausableGameplayContainer => PausableGameplayContainer;
+
+ public UserDimContainer CurrentStoryboardContainer => StoryboardContainer;
+
+ // Whether or not the player should be allowed to load.
+ public bool Ready;
+
+ public Bindable StoryboardEnabled;
+ public readonly Bindable ReplacesBackground = new Bindable();
+ public readonly Bindable IsPaused = new Bindable();
+
+ public bool IsStoryboardVisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha == 1;
+
+ public bool IsStoryboardInvisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha <= 1;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config)
+ {
+ while (!Ready)
+ Thread.Sleep(1);
+ StoryboardEnabled = config.GetBindable(OsuSetting.ShowStoryboard);
+ ReplacesBackground.BindTo(Background.StoryboardReplacesBackground);
+ RulesetContainer.IsPaused.BindTo(IsPaused);
+ }
+ }
+
+ private class ScreenStackCacheContainer : Container
+ {
+ [Cached]
+ private BackgroundScreenStack backgroundScreenStack;
+
+ public readonly ScreenStack ScreenStack;
+
+ public ScreenStackCacheContainer()
+ {
+ Add(backgroundScreenStack = new BackgroundScreenStack { RelativeSizeAxes = Axes.Both });
+ Add(ScreenStack = new ScreenStack { RelativeSizeAxes = Axes.Both });
+ }
+ }
+
+ private class DimAccessiblePlayerLoader : PlayerLoader
+ {
+ public VisualSettings VisualSettingsPos => VisualSettings;
+ public BackgroundScreen ScreenPos => Background;
+
+ public DimAccessiblePlayerLoader(Player player)
+ : base(() => player)
+ {
+ }
+
+ public void TriggerOnHover() => OnHover(new HoverEvent(new InputState()));
+
+ protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
+ }
+
+ private class FadeAccessibleBackground : BackgroundScreenBeatmap
+ {
+ protected override UserDimContainer CreateFadeContainer() => fadeContainer = new TestUserDimContainer { RelativeSizeAxes = Axes.Both };
+
+ public Color4 CurrentColour => fadeContainer.CurrentColour;
+ public float CurrentAlpha => fadeContainer.CurrentAlpha;
+
+ public Vector2 CurrentBlur => Background.BlurSigma;
+
+ private TestUserDimContainer fadeContainer;
+
+ public FadeAccessibleBackground(WorkingBeatmap beatmap)
+ : base(beatmap)
+ {
+ }
+ }
+
+ private class TestUserDimContainer : UserDimContainer
+ {
+ public Color4 CurrentColour => DimContainer.Colour;
+ public float CurrentAlpha => DimContainer.Alpha;
+
+ public TestUserDimContainer(bool isStoryboard = false)
+ : base(isStoryboard)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs
index 85ef5963a5..2fd8d467f6 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs
@@ -15,6 +15,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osuTK.Graphics;
using osu.Framework.Lists;
+using osu.Game.Graphics;
namespace osu.Game.Tests.Visual
{
@@ -140,6 +141,7 @@ namespace osu.Game.Tests.Visual
}
private SortedList timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints;
+
private TimingControlPoint getNextTimingPoint(TimingControlPoint current)
{
if (timingPoints[timingPoints.Count - 1] == current)
@@ -189,15 +191,15 @@ namespace osu.Game.Tests.Visual
public double Value
{
- set { valueText.Text = $"{value:G}"; }
+ set => valueText.Text = $"{value:G}";
}
public InfoString(string header)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
- Add(new OsuSpriteText { Text = header + @": ", TextSize = text_size });
- Add(valueText = new OsuSpriteText { TextSize = text_size });
+ Add(new OsuSpriteText { Text = header + @": ", Font = OsuFont.GetFont(size: text_size) });
+ Add(valueText = new OsuSpriteText { Font = OsuFont.GetFont(size: text_size) });
Margin = new MarginPadding(margin);
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
index e61bca9846..618d8376c0 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
@@ -147,9 +147,10 @@ namespace osu.Game.Tests.Visual
private bool selectedBeatmapVisible()
{
- var currentlySelected = carousel.Items.Find(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected);
+ var currentlySelected = carousel.Items.Find(s => s.Item is CarouselBeatmap && s.Item.State.Value == CarouselItemState.Selected);
if (currentlySelected == null)
return true;
+
return currentlySelected.Item.Visible;
}
@@ -166,8 +167,7 @@ namespace osu.Game.Tests.Visual
carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false);
carousel.Filter(new FilterCriteria(), false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
- }
- );
+ });
}
///
@@ -522,6 +522,7 @@ namespace osu.Game.Tests.Visual
}
});
}
+
return toReturn;
}
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs b/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs
index bc4f89ac26..84af6453f5 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual
Padding = new MarginPadding(150),
});
- AddStep("beatmap all metrics", () => details.Beatmap = new BeatmapInfo
+ AddStep("all metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "All Metrics",
Metadata = new BeatmapMetadata
@@ -45,7 +45,30 @@ namespace osu.Game.Tests.Visual
},
});
- AddStep("beatmap ratings", () => details.Beatmap = new BeatmapInfo
+ AddStep("all except source", () => details.Beatmap = new BeatmapInfo
+ {
+ Version = "All Metrics",
+ Metadata = new BeatmapMetadata
+ {
+ Tags = "this beatmap has all the metrics",
+ },
+ BaseDifficulty = new BeatmapDifficulty
+ {
+ CircleSize = 7,
+ DrainRate = 1,
+ OverallDifficulty = 5.7f,
+ ApproachRate = 3.5f,
+ },
+ StarDifficulty = 5.3f,
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 11),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ });
+
+ AddStep("ratings", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Ratings",
Metadata = new BeatmapMetadata
@@ -67,7 +90,7 @@ namespace osu.Game.Tests.Visual
},
});
- AddStep("beatmap fails retries", () => details.Beatmap = new BeatmapInfo
+ AddStep("fails retries", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Retries and Fails",
Metadata = new BeatmapMetadata
@@ -90,7 +113,7 @@ namespace osu.Game.Tests.Visual
},
});
- AddStep("beatmap no metrics", () => details.Beatmap = new BeatmapInfo
+ AddStep("no metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "No Metrics",
Metadata = new BeatmapMetadata
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
index d3056f0b13..31bb8b64a3 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual
AddStep("show", () =>
{
infoWedge.State = Visibility.Visible;
- infoWedge.Beatmap = Beatmap;
+ infoWedge.Beatmap = Beatmap.Value;
});
// select part is redundant, but wait for load isn't
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
index 321a38d087..bb55c0b1e8 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
@@ -23,9 +23,6 @@ namespace osu.Game.Tests.Visual
[System.ComponentModel.Description("in BeatmapOverlay")]
public class TestCaseBeatmapScoresContainer : OsuTestCase
{
- private readonly IEnumerable scores;
- private readonly IEnumerable anotherScores;
- private readonly APIScoreInfo topScoreInfo;
private readonly Box background;
public TestCaseBeatmapScoresContainer()
@@ -47,15 +44,7 @@ namespace osu.Game.Tests.Visual
}
};
- AddStep("scores pack 1", () => scoresContainer.Scores = scores);
- AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
- AddStep("only top score", () => scoresContainer.Scores = new[] { topScoreInfo });
- AddStep("remove scores", () => scoresContainer.Scores = null);
- AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
- AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
- AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
-
- scores = new[]
+ IEnumerable scores = new[]
{
new APIScoreInfo
{
@@ -160,14 +149,15 @@ namespace osu.Game.Tests.Visual
Accuracy = 0.6543,
},
};
- foreach(var s in scores)
+
+ foreach (var s in scores)
{
s.Statistics.Add(HitResult.Great, RNG.Next(2000));
s.Statistics.Add(HitResult.Good, RNG.Next(2000));
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
}
- anotherScores = new[]
+ IEnumerable anotherScores = new[]
{
new APIScoreInfo
{
@@ -279,7 +269,7 @@ namespace osu.Game.Tests.Visual
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
}
- topScoreInfo = new APIScoreInfo
+ var topScoreInfo = new APIScoreInfo
{
User = new User
{
@@ -304,6 +294,14 @@ namespace osu.Game.Tests.Visual
topScoreInfo.Statistics.Add(HitResult.Great, RNG.Next(2000));
topScoreInfo.Statistics.Add(HitResult.Good, RNG.Next(2000));
topScoreInfo.Statistics.Add(HitResult.Meh, RNG.Next(2000));
+
+ AddStep("scores pack 1", () => scoresContainer.Scores = scores);
+ AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
+ AddStep("only top score", () => scoresContainer.Scores = new[] { topScoreInfo });
+ AddStep("remove scores", () => scoresContainer.Scores = null);
+ AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
+ AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
+ AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs
index 6e60eb3306..749303b1bb 100644
--- a/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs
+++ b/osu.Game.Tests/Visual/TestCaseChannelTabControl.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual
});
channelTabControl.OnRequestLeave += channel => channelTabControl.RemoveChannel(channel);
- channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.ToString();
+ channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue.ToString();
AddStep("Add random private channel", addRandomPrivateChannel);
AddAssert("There is only one channels", () => channelTabControl.Items.Count() == 2);
diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs
index f119e6dc24..b2ec2c9b47 100644
--- a/osu.Game.Tests/Visual/TestCaseChatLink.cs
+++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs
@@ -13,10 +13,10 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Bindables;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
-using osu.Framework.Configuration;
namespace osu.Game.Tests.Visual
{
@@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual
var chatManager = new ChannelManager();
BindableList availableChannels = (BindableList)chatManager.AvailableChannels;
- availableChannels.Add(new Channel { Name = "#english"});
+ availableChannels.Add(new Channel { Name = "#english" });
availableChannels.Add(new Channel { Name = "#japanese" });
Dependencies.Cache(chatManager);
@@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual
return true;
}
- bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font == "Exo2.0-MediumItalic");
+ bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics);
bool isShowingLinks()
{
diff --git a/osu.Game.Tests/Visual/TestCaseContextMenu.cs b/osu.Game.Tests/Visual/TestCaseContextMenu.cs
index f969ec69e2..5cbe97e21d 100644
--- a/osu.Game.Tests/Visual/TestCaseContextMenu.cs
+++ b/osu.Game.Tests/Visual/TestCaseContextMenu.cs
@@ -61,10 +61,10 @@ namespace osu.Game.Tests.Visual
// Move box along a square trajectory
container.Loop(c => c
- .MoveTo(new Vector2(0, 100), duration).Then()
- .MoveTo(new Vector2(100, 100), duration).Then()
- .MoveTo(new Vector2(100, 0), duration).Then()
- .MoveTo(Vector2.Zero, duration)
+ .MoveTo(new Vector2(0, 100), duration).Then()
+ .MoveTo(new Vector2(100, 100), duration).Then()
+ .MoveTo(new Vector2(100, 0), duration).Then()
+ .MoveTo(Vector2.Zero, duration)
);
}
diff --git a/osu.Game.Tests/Visual/TestCaseDirectPanel.cs b/osu.Game.Tests/Visual/TestCaseDirectPanel.cs
new file mode 100644
index 0000000000..beb88ac56c
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseDirectPanel.cs
@@ -0,0 +1,47 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays.Direct;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Tests.Beatmaps;
+using osuTK;
+
+namespace osu.Game.Tests.Visual
+{
+ public class TestCaseDirectPanel : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DirectGridPanel),
+ typeof(DirectListPanel),
+ typeof(IconPill)
+ };
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ var beatmap = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null);
+ beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true;
+ beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true;
+
+ Child = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Padding = new MarginPadding(20),
+ Spacing = new Vector2(0, 20),
+ Children = new Drawable[]
+ {
+ new DirectGridPanel(beatmap.BeatmapSetInfo),
+ new DirectListPanel(beatmap.BeatmapSetInfo)
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseDisclaimer.cs b/osu.Game.Tests/Visual/TestCaseDisclaimer.cs
index d3a53e4a05..3ceb3eb4bd 100644
--- a/osu.Game.Tests/Visual/TestCaseDisclaimer.cs
+++ b/osu.Game.Tests/Visual/TestCaseDisclaimer.cs
@@ -2,27 +2,16 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Menu;
-using osuTK.Graphics;
namespace osu.Game.Tests.Visual
{
- public class TestCaseDisclaimer : OsuTestCase
+ public class TestCaseDisclaimer : ScreenTestCase
{
[BackgroundDependencyLoader]
private void load()
{
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
- new Disclaimer()
- };
+ LoadScreen(new Disclaimer());
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseDrawableDate.cs b/osu.Game.Tests/Visual/TestCaseDrawableDate.cs
index 839f2a82de..8d2182dd78 100644
--- a/osu.Game.Tests/Visual/TestCaseDrawableDate.cs
+++ b/osu.Game.Tests/Visual/TestCaseDrawableDate.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual
}
};
- drawableDate.Current.ValueChanged += v => flash.FadeOutFromOne(500);
+ drawableDate.Current.ValueChanged += _ => flash.FadeOutFromOne(500);
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseDrawings.cs b/osu.Game.Tests/Visual/TestCaseDrawings.cs
index 51f34d54db..aad135b71f 100644
--- a/osu.Game.Tests/Visual/TestCaseDrawings.cs
+++ b/osu.Game.Tests/Visual/TestCaseDrawings.cs
@@ -9,11 +9,11 @@ using osu.Game.Screens.Tournament.Teams;
namespace osu.Game.Tests.Visual
{
[Description("for tournament use")]
- public class TestCaseDrawings : OsuTestCase
+ public class TestCaseDrawings : ScreenTestCase
{
public TestCaseDrawings()
{
- Add(new Drawings
+ LoadScreen(new Drawings
{
TeamList = new TestTeamList(),
});
diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs
index 66e13545d9..a52454d684 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load()
{
- Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
+ Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, Clock);
Child = new ComposeScreen();
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs
index 7e6edf78fc..9ae9b55546 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs
@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual
}
[BackgroundDependencyLoader]
- private void load(IAdjustableClock adjustableClock, IBindableBeatmap beatmap)
+ private void load(IAdjustableClock adjustableClock, IBindable beatmap)
{
this.adjustableClock = adjustableClock;
this.beatmap.BindTo(beatmap);
diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs
index 6cb9a1abfd..0ec87e6f52 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual
{
TimingPoints =
{
- new TimingControlPoint { Time = 0, BeatLength = 200},
+ new TimingControlPoint { Time = 0, BeatLength = 200 },
new TimingControlPoint { Time = 100, BeatLength = 400 },
new TimingControlPoint { Time = 175, BeatLength = 800 },
new TimingControlPoint { Time = 350, BeatLength = 200 },
@@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual
}
};
- Beatmap.Value = new TestWorkingBeatmap(testBeatmap);
+ Beatmap.Value = new TestWorkingBeatmap(testBeatmap, Clock);
Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };
}
diff --git a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs
index b952582ef2..305924958b 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load()
{
- Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
+ Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null);
Add(new SummaryTimeline
{
diff --git a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
index c5abe03dbd..93a059d214 100644
--- a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
@@ -17,15 +17,15 @@ namespace osu.Game.Tests.Visual
[Description("player pause/fail screens")]
public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase
{
- public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) };
+ public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PausableGameplayContainer) };
private FailOverlay failOverlay;
- private PauseContainer.PauseOverlay pauseOverlay;
+ private PausableGameplayContainer.PauseOverlay pauseOverlay;
[BackgroundDependencyLoader]
private void load()
{
- Add(pauseOverlay = new PauseContainer.PauseOverlay
+ Add(pauseOverlay = new PausableGameplayContainer.PauseOverlay
{
OnResume = () => Logger.Log(@"Resume"),
OnRetry = () => Logger.Log(@"Retry"),
@@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual
AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First()));
AddStep("Hide overlay", () => failOverlay.Hide());
- AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected));
+ AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected.Value));
}
private void press(Key key)
@@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual
AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Up arrow", () => press(Key.Up));
- AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected);
+ AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected.Value);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
@@ -119,7 +119,7 @@ namespace osu.Game.Tests.Visual
AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Down arrow", () => press(Key.Down));
- AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
+ AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected.Value);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
@@ -132,11 +132,11 @@ namespace osu.Game.Tests.Visual
AddStep("Show overlay", () => failOverlay.Show());
AddStep("Up arrow", () => press(Key.Up));
- AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
+ AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value);
AddStep("Up arrow", () => press(Key.Up));
- AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
+ AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value);
AddStep("Up arrow", () => press(Key.Up));
- AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
+ AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value);
AddStep("Hide overlay", () => failOverlay.Hide());
}
@@ -149,11 +149,11 @@ namespace osu.Game.Tests.Visual
AddStep("Show overlay", () => failOverlay.Show());
AddStep("Down arrow", () => press(Key.Down));
- AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
+ AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value);
AddStep("Down arrow", () => press(Key.Down));
- AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
+ AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value);
AddStep("Down arrow", () => press(Key.Down));
- AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
+ AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value);
AddStep("Hide overlay", () => failOverlay.Hide());
}
@@ -169,8 +169,8 @@ namespace osu.Game.Tests.Visual
AddStep("Down arrow", () => press(Key.Down));
AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
- AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected);
- AddAssert("Second button selected", () => secondButton.Selected);
+ AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected.Value);
+ AddAssert("Second button selected", () => secondButton.Selected.Value);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
@@ -190,8 +190,8 @@ namespace osu.Game.Tests.Visual
AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
AddStep("Up arrow", () => press(Key.Up));
- AddAssert("Second button not selected", () => !secondButton.Selected);
- AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
+ AddAssert("Second button not selected", () => !secondButton.Selected.Value);
+ AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected.Value);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
@@ -208,7 +208,7 @@ namespace osu.Game.Tests.Visual
AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero));
AddStep("Down arrow", () => press(Key.Down));
- AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition
+ AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected.Value); // Initial state condition
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
@@ -265,6 +265,7 @@ namespace osu.Game.Tests.Visual
pauseOverlay.OnRetry = lastAction;
lastAction = null;
}
+
return triggered;
});
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
diff --git a/osu.Game.Tests/Visual/TestCaseHoldToConfirmOverlay.cs b/osu.Game.Tests/Visual/TestCaseHoldToConfirmOverlay.cs
index 046bd76f7e..f66bf34875 100644
--- a/osu.Game.Tests/Visual/TestCaseHoldToConfirmOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseHoldToConfirmOverlay.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
+using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Menu;
@@ -22,7 +23,7 @@ namespace osu.Game.Tests.Visual
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "Fired!",
- TextSize = 50,
+ Font = OsuFont.GetFont(size: 50),
Alpha = 0,
};
diff --git a/osu.Game.Tests/Visual/TestCaseIdleTracker.cs b/osu.Game.Tests/Visual/TestCaseIdleTracker.cs
index ea669af28c..8e8c4e38ae 100644
--- a/osu.Game.Tests/Visual/TestCaseIdleTracker.cs
+++ b/osu.Game.Tests/Visual/TestCaseIdleTracker.cs
@@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual
},
};
- idleTracker.IsIdle.BindValueChanged(idle => box.Colour = idle ? Color4.White : Color4.Black, true);
+ idleTracker.IsIdle.BindValueChanged(idle => box.Colour = idle.NewValue ? Color4.White : Color4.Black, true);
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs
index 822809b96e..eb1a2c0249 100644
--- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs
+++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs
@@ -20,7 +20,8 @@ namespace osu.Game.Tests.Visual
[Description("PlaySongSelect leaderboard")]
public class TestCaseLeaderboard : OsuTestCase
{
- public override IReadOnlyList RequiredTypes => new[] {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
typeof(Placeholder),
typeof(MessagePlaceholder),
typeof(RetrievalFailurePlaceholder),
diff --git a/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs b/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs
index 70ed075101..2088f97580 100644
--- a/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs
+++ b/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs
@@ -4,6 +4,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Screens;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osuTK.Graphics;
@@ -57,7 +58,7 @@ namespace osu.Game.Tests.Visual
public OsuLogo Logo;
private TestScreen screen;
- public bool ScreenLoaded => screen.IsCurrentScreen;
+ public bool ScreenLoaded => screen.IsCurrentScreen();
public TestLoader(double delay)
{
@@ -96,7 +97,7 @@ namespace osu.Game.Tests.Visual
{
public TestScreen()
{
- Child = new Box
+ InternalChild = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.DarkSlateGray,
@@ -107,7 +108,7 @@ namespace osu.Game.Tests.Visual
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
- Child.FadeInFromZero(200);
+ InternalChild.FadeInFromZero(200);
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs
index 1dfc3cdc60..13bc5e24d9 100644
--- a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs
@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Online.Multiplayer;
@@ -16,7 +16,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual
{
- public class TestCaseLoungeRoomsContainer : OsuTestCase
+ public class TestCaseLoungeRoomsContainer : MultiplayerTestCase
{
public override IReadOnlyList RequiredTypes => new[]
{
@@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0));
AddStep("select first room", () => container.Rooms.First().Action?.Invoke());
- AddAssert("first room selected", () => container.SelectedRoom.Value == roomManager.Rooms.First());
+ AddAssert("first room selected", () => Room == roomManager.Rooms.First());
AddStep("join first room", () => container.Rooms.First().Action?.Invoke());
AddAssert("first room joined", () => roomManager.Rooms.First().Status.Value is JoinedRoomStatus);
@@ -71,7 +71,11 @@ namespace osu.Game.Tests.Visual
private class TestRoomManager : IRoomManager
{
- public event Action RoomsUpdated;
+ public event Action RoomsUpdated
+ {
+ add { }
+ remove { }
+ }
public readonly BindableList Rooms = new BindableList();
IBindableList IRoomManager.Rooms => Rooms;
@@ -85,10 +89,6 @@ namespace osu.Game.Tests.Visual
public void PartRoom()
{
}
-
- public void Filter(FilterCriteria criteria)
- {
- }
}
private class JoinedRoomStatus : RoomStatus
diff --git a/osu.Game.Tests/Visual/TestCaseMatchHeader.cs b/osu.Game.Tests/Visual/TestCaseMatchHeader.cs
index c1664c99a3..296e5f24ac 100644
--- a/osu.Game.Tests/Visual/TestCaseMatchHeader.cs
+++ b/osu.Game.Tests/Visual/TestCaseMatchHeader.cs
@@ -12,7 +12,7 @@ using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
- public class TestCaseMatchHeader : OsuTestCase
+ public class TestCaseMatchHeader : MultiplayerTestCase
{
public override IReadOnlyList RequiredTypes => new[]
{
@@ -21,11 +21,7 @@ namespace osu.Game.Tests.Visual
public TestCaseMatchHeader()
{
- var room = new Room();
-
- var header = new Header(room);
-
- room.Playlist.Add(new PlaylistItem
+ Room.Playlist.Add(new PlaylistItem
{
Beatmap = new BeatmapInfo
{
@@ -46,9 +42,9 @@ namespace osu.Game.Tests.Visual
}
});
- room.Type.Value = new GameTypeTimeshift();
+ Room.Type.Value = new GameTypeTimeshift();
- Child = header;
+ Child = new Header();
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseMatchHostInfo.cs b/osu.Game.Tests/Visual/TestCaseMatchHostInfo.cs
index fabeb0aaa4..45092c5b93 100644
--- a/osu.Game.Tests/Visual/TestCaseMatchHostInfo.cs
+++ b/osu.Game.Tests/Visual/TestCaseMatchHostInfo.cs
@@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
diff --git a/osu.Game.Tests/Visual/TestCaseMatchInfo.cs b/osu.Game.Tests/Visual/TestCaseMatchInfo.cs
index 57b21f2d79..901c4f1644 100644
--- a/osu.Game.Tests/Visual/TestCaseMatchInfo.cs
+++ b/osu.Game.Tests/Visual/TestCaseMatchInfo.cs
@@ -14,7 +14,7 @@ using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
[TestFixture]
- public class TestCaseMatchInfo : OsuTestCase
+ public class TestCaseMatchInfo : MultiplayerTestCase
{
public override IReadOnlyList RequiredTypes => new[]
{
@@ -27,18 +27,15 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
- var room = new Room();
+ Add(new Info());
- Info info = new Info(room);
- Add(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 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
+ Room.Playlist.Clear();
+ Room.Playlist.Add(new PlaylistItem
{
Beatmap = new BeatmapInfo
{
@@ -54,14 +51,14 @@ namespace osu.Game.Tests.Visual
});
});
- 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 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
+ Room.Playlist.Clear();
+ Room.Playlist.Add(new PlaylistItem
{
Beatmap = new BeatmapInfo
{
diff --git a/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs
index 110c7699cb..42a886a5a3 100644
--- a/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs
+++ b/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs
@@ -6,24 +6,24 @@ using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Online.API;
-using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual
{
- public class TestCaseMatchLeaderboard : OsuTestCase
+ public class TestCaseMatchLeaderboard : MultiplayerTestCase
{
public TestCaseMatchLeaderboard()
{
+ Room.RoomID.Value = 3;
+
Add(new MatchLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = MatchLeaderboardScope.Overall,
- Room = new Room { RoomID = { Value = 3 } }
});
}
diff --git a/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs b/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs
index 174f39a702..716523c23c 100644
--- a/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs
+++ b/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs
@@ -1,9 +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.Generic;
using NUnit.Framework;
-using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
@@ -11,22 +9,14 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
- public class TestCaseMatchParticipants : OsuTestCase
+ public class TestCaseMatchParticipants : MultiplayerTestCase
{
- private readonly Bindable maxParticipants = new Bindable();
- private readonly Bindable> users = new Bindable>();
-
public TestCaseMatchParticipants()
{
- Participants participants;
+ Add(new Participants { RelativeSizeAxes = Axes.Both });
- Add(participants = new Participants { RelativeSizeAxes = Axes.Both });
-
- participants.MaxParticipants.BindTo(maxParticipants);
- participants.Users.BindTo(users);
-
- AddStep(@"set max to null", () => maxParticipants.Value = null);
- AddStep(@"set users", () => users.Value = new[]
+ AddStep(@"set max to null", () => Room.MaxParticipants.Value = null);
+ AddStep(@"set users", () => Room.Participants.Value = new[]
{
new User
{
@@ -54,9 +44,9 @@ namespace osu.Game.Tests.Visual
},
});
- AddStep(@"set max", () => maxParticipants.Value = 10);
- AddStep(@"clear users", () => users.Value = new User[] { });
- AddStep(@"set max to null", () => maxParticipants.Value = null);
+ AddStep(@"set max", () => Room.MaxParticipants.Value = 10);
+ AddStep(@"clear users", () => Room.Participants.Value = new User[] { });
+ AddStep(@"set max to null", () => Room.MaxParticipants.Value = null);
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseMatchResults.cs b/osu.Game.Tests/Visual/TestCaseMatchResults.cs
index 3ce03cf723..582c035e82 100644
--- a/osu.Game.Tests/Visual/TestCaseMatchResults.cs
+++ b/osu.Game.Tests/Visual/TestCaseMatchResults.cs
@@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
-using osu.Game.Online.Multiplayer;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Screens.Multi.Ranking;
@@ -19,7 +18,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
- public class TestCaseMatchResults : OsuTestCase
+ public class TestCaseMatchResults : MultiplayerTestCase
{
public override IReadOnlyList RequiredTypes => new[]
{
@@ -38,68 +37,52 @@ namespace osu.Game.Tests.Visual
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
- Child = new TestMatchResults(new ScoreInfo
+ Room.RoomID.Value = 1;
+ Room.Name.Value = "an awesome room";
+
+ LoadScreen(new TestMatchResults(new ScoreInfo
{
User = new User { Id = 10 },
- });
+ }));
}
private class TestMatchResults : MatchResults
{
- private readonly Room room;
-
public TestMatchResults(ScoreInfo score)
- : this(score, new Room
- {
- RoomID = { Value = 1 },
- Name = { Value = "an awesome room" }
- })
+ : base(score)
{
}
- public TestMatchResults(ScoreInfo score, Room room)
- : base(score, room)
- {
- this.room = room;
- }
-
- protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap, room) };
+ protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value) };
}
private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
- private readonly Room room;
- public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap, Room room)
- : base(score, beatmap, room)
+ public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
+ : base(score, beatmap)
{
this.score = score;
this.beatmap = beatmap;
- this.room = room;
}
- public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap, room);
+ public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap);
}
private class TestRoomLeaderboardPage : RoomLeaderboardPage
{
- public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap, Room room)
- : base(score, beatmap, room)
+ public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap)
+ : base(score, beatmap)
{
}
- protected override MatchLeaderboard CreateLeaderboard(Room room) => new TestMatchLeaderboard(room);
+ protected override MatchLeaderboard CreateLeaderboard() => new TestMatchLeaderboard();
}
private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard
{
- public TestMatchLeaderboard(Room room)
- : base(room)
- {
- }
-
protected override APIRequest FetchScores(Action> scoresCallback)
{
var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray();
diff --git a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs
index 16240f0c45..a320fc88fa 100644
--- a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs
@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
@@ -13,12 +13,11 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi;
-using osu.Game.Screens.Multi.Lounge.Components;
using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
- public class TestCaseMatchSettingsOverlay : OsuTestCase
+ public class TestCaseMatchSettingsOverlay : MultiplayerTestCase
{
public override IReadOnlyList RequiredTypes => new[]
{
@@ -28,14 +27,14 @@ namespace osu.Game.Tests.Visual
[Cached(Type = typeof(IRoomManager))]
private TestRoomManager roomManager = new TestRoomManager();
- private Room room;
private TestRoomSettings settings;
[SetUp]
public void Setup() => Schedule(() =>
{
- room = new Room();
- settings = new TestRoomSettings(room)
+ Room = new Room();
+
+ settings = new TestRoomSettings
{
RelativeSizeAxes = Axes.Both,
State = Visibility.Visible
@@ -49,20 +48,20 @@ namespace osu.Game.Tests.Visual
{
AddStep("clear name and beatmap", () =>
{
- room.Name.Value = "";
- room.Playlist.Clear();
+ Room.Name.Value = "";
+ Room.Playlist.Clear();
});
- AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
+ AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
- AddStep("set name", () => room.Name.Value = "Room name");
- AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
+ AddStep("set name", () => Room.Name.Value = "Room name");
+ AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
- AddStep("set beatmap", () => room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo }));
- AddAssert("button enabled", () => settings.ApplyButton.Enabled);
+ AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo }));
+ AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
- AddStep("clear name", () => room.Name.Value = "");
- AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
+ AddStep("clear name", () => Room.Name.Value = "");
+ AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
}
[Test]
@@ -117,17 +116,12 @@ namespace osu.Game.Tests.Visual
private class TestRoomSettings : MatchSettingsOverlay
{
- public new TriangleButton ApplyButton => base.ApplyButton;
+ public TriangleButton ApplyButton => Settings.ApplyButton;
- public new OsuTextBox NameField => base.NameField;
- public new OsuDropdown DurationField => base.DurationField;
+ public OsuTextBox NameField => Settings.NameField;
+ public OsuDropdown DurationField => Settings.DurationField;
- public new OsuSpriteText ErrorText => base.ErrorText;
-
- public TestRoomSettings(Room room)
- : base(room)
- {
- }
+ public OsuSpriteText ErrorText => Settings.ErrorText;
}
private class TestRoomManager : IRoomManager
@@ -136,7 +130,11 @@ namespace osu.Game.Tests.Visual
public Func CreateRequested;
- public event Action RoomsUpdated;
+ public event Action RoomsUpdated
+ {
+ add { }
+ remove { }
+ }
public IBindableList Rooms { get; } = null;
@@ -154,8 +152,6 @@ namespace osu.Game.Tests.Visual
public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => throw new NotImplementedException();
public void PartRoom() => throw new NotImplementedException();
-
- public void Filter(FilterCriteria criteria) => throw new NotImplementedException();
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs
index e0c0b419af..99bc10d8cc 100644
--- a/osu.Game.Tests/Visual/TestCaseMods.cs
+++ b/osu.Game.Tests/Visual/TestCaseMods.cs
@@ -13,7 +13,7 @@ using osu.Game.Rulesets.Osu.Mods;
using System.Linq;
using System.Collections.Generic;
using NUnit.Framework;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Mods.Sections;
diff --git a/osu.Game.Tests/Visual/TestCaseMultiHeader.cs b/osu.Game.Tests/Visual/TestCaseMultiHeader.cs
index 998cdcbad1..f7802e2d08 100644
--- a/osu.Game.Tests/Visual/TestCaseMultiHeader.cs
+++ b/osu.Game.Tests/Visual/TestCaseMultiHeader.cs
@@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
+using osu.Framework.Screens;
using osu.Game.Screens;
using osu.Game.Screens.Multi;
@@ -15,15 +16,15 @@ namespace osu.Game.Tests.Visual
{
int index = 0;
- OsuScreen currentScreen = new TestMultiplayerSubScreen(index);
+ ScreenStack screenStack = new ScreenStack(new TestMultiplayerSubScreen(index)) { RelativeSizeAxes = Axes.Both };
Children = new Drawable[]
{
- currentScreen,
- new Header(currentScreen)
+ screenStack,
+ new Header(screenStack)
};
- AddStep("push multi screen", () => currentScreen.Push(currentScreen = new TestMultiplayerSubScreen(++index)));
+ AddStep("push multi screen", () => screenStack.CurrentScreen.Push(new TestMultiplayerSubScreen(++index)));
}
private class TestMultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen
diff --git a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs
index 13419167a7..804e3c5b1f 100644
--- a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs
+++ b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs
@@ -11,7 +11,7 @@ using osu.Game.Screens.Multi.Lounge.Components;
namespace osu.Game.Tests.Visual
{
[TestFixture]
- public class TestCaseMultiScreen : OsuTestCase
+ public class TestCaseMultiScreen : ScreenTestCase
{
public override IReadOnlyList RequiredTypes => new[]
{
@@ -24,9 +24,7 @@ namespace osu.Game.Tests.Visual
{
Multiplayer multi = new Multiplayer();
- AddStep(@"show", () => Add(multi));
- AddWaitStep(5);
- AddStep(@"exit", multi.Exit);
+ AddStep(@"show", () => LoadScreen(multi));
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
index 109c2ed916..d6a3361cf2 100644
--- a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
@@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual
void setState(Visibility state) => AddStep(state.ToString(), () => manager.State = state);
void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected);
- manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; };
+ manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; };
setState(Visibility.Visible);
AddStep(@"simple #1", sendHelloNotification);
diff --git a/osu.Game.Tests/Visual/TestCaseOsuGame.cs b/osu.Game.Tests/Visual/TestCaseOsuGame.cs
index 16087b5ad8..9e649b92e4 100644
--- a/osu.Game.Tests/Visual/TestCaseOsuGame.cs
+++ b/osu.Game.Tests/Visual/TestCaseOsuGame.cs
@@ -4,9 +4,10 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Screens;
+using osu.Framework.Platform;
using osu.Game.Screens.Menu;
using osuTK.Graphics;
@@ -20,8 +21,12 @@ namespace osu.Game.Tests.Visual
typeof(OsuLogo),
};
- public TestCaseOsuGame()
+ [BackgroundDependencyLoader]
+ private void load(GameHost host)
{
+ OsuGame game = new OsuGame();
+ game.SetHost(host);
+
Children = new Drawable[]
{
new Box
@@ -29,7 +34,7 @@ namespace osu.Game.Tests.Visual
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
- new Loader()
+ game
};
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs b/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs
index f9804655ea..41b029d69e 100644
--- a/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseParallaxContainer.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 osu.Framework.Graphics;
+using osu.Framework.Screens;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Backgrounds;
@@ -14,7 +16,10 @@ namespace osu.Game.Tests.Visual
Add(parallax = new ParallaxContainer
{
- Child = new BackgroundScreenDefault { Alpha = 0.8f }
+ Child = new ScreenStack(new BackgroundScreenDefault { Alpha = 0.8f })
+ {
+ RelativeSizeAxes = Axes.Both,
+ }
});
AddStep("default parallax", () => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT);
diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
index dedcc24e53..5fa818472c 100644
--- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
@@ -8,10 +8,11 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
using osu.Framework.Platform;
+using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
@@ -25,7 +26,7 @@ using osu.Game.Screens.Select.Filter;
namespace osu.Game.Tests.Visual
{
[TestFixture]
- public class TestCasePlaySongSelect : OsuTestCase
+ public class TestCasePlaySongSelect : ScreenTestCase
{
private BeatmapManager manager;
@@ -102,21 +103,16 @@ namespace osu.Game.Tests.Visual
}
[SetUp]
- public virtual void SetUp()
- {
- Schedule(() =>
- {
- manager?.Delete(manager.GetAllUsableBeatmapSets());
- Child = songSelect = new TestSongSelect();
- });
- }
+ public virtual void SetUp() =>
+ Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); });
[Test]
public void TestDummy()
{
+ createSongSelect();
AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap);
- AddAssert("dummy shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap);
+ AddUntilStep(() => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap, "dummy shown on wedge");
addManyTestMaps();
AddWaitStep(3);
@@ -127,6 +123,7 @@ namespace osu.Game.Tests.Visual
[Test]
public void TestSorting()
{
+ createSongSelect();
addManyTestMaps();
AddWaitStep(3);
@@ -142,6 +139,7 @@ namespace osu.Game.Tests.Visual
[Ignore("needs fixing")]
public void TestImportUnderDifferentRuleset()
{
+ createSongSelect();
changeRuleset(2);
importForRuleset(0);
AddUntilStep(() => songSelect.Carousel.SelectedBeatmap == null, "no selection");
@@ -150,6 +148,7 @@ namespace osu.Game.Tests.Visual
[Test]
public void TestImportUnderCurrentRuleset()
{
+ createSongSelect();
changeRuleset(2);
importForRuleset(2);
importForRuleset(1);
@@ -165,6 +164,7 @@ namespace osu.Game.Tests.Visual
[Test]
public void TestRulesetChangeResetsMods()
{
+ createSongSelect();
changeRuleset(0);
changeMods(new OsuModHardRock());
@@ -187,13 +187,14 @@ namespace osu.Game.Tests.Visual
AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex);
AddAssert("empty mods", () => !selectedMods.Value.Any());
- void onModChange(IEnumerable mods) => modChangeIndex = actionIndex++;
- void onRulesetChange(RulesetInfo ruleset) => rulesetChangeIndex = actionIndex--;
+ void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++;
+ void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex--;
}
[Test]
public void TestStartAfterUnMatchingFilterDoesNotStart()
{
+ createSongSelect();
addManyTestMaps();
AddUntilStep(() => songSelect.Carousel.SelectedBeatmap != null, "has selection");
@@ -221,6 +222,12 @@ namespace osu.Game.Tests.Visual
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
+ private void createSongSelect()
+ {
+ AddStep("create song select", () => LoadScreen(songSelect = new TestSongSelect()));
+ AddUntilStep(() => songSelect.IsCurrentScreen(), "wait for present");
+ }
+
private void addManyTestMaps()
{
AddStep("import test maps", () =>
diff --git a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs
index 15b96d394a..abcff24c67 100644
--- a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs
+++ b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs
@@ -26,10 +26,10 @@ namespace osu.Game.Tests.Visual
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Size = new Vector2(200,100)
+ Size = new Vector2(200, 100)
};
- Beatmap.Value = new TestWorkingBeatmap(new Beatmap());
+ Beatmap.Value = new TestWorkingBeatmap(new Beatmap(), Clock);
Child = playback;
}
diff --git a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs
index fd3d838149..244f553e97 100644
--- a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs
@@ -3,7 +3,10 @@
using System.Threading;
using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Screens;
using osu.Game.Beatmaps;
+using osu.Game.Screens;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
@@ -11,28 +14,44 @@ namespace osu.Game.Tests.Visual
public class TestCasePlayerLoader : ManualInputManagerTestCase
{
private PlayerLoader loader;
+ private readonly ScreenStack stack;
+
+ [Cached]
+ private BackgroundScreenStack backgroundStack;
+
+ public TestCasePlayerLoader()
+ {
+ InputManager.Add(backgroundStack = new BackgroundScreenStack { RelativeSizeAxes = Axes.Both });
+ InputManager.Add(stack = new ScreenStack { RelativeSizeAxes = Axes.Both });
+ }
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
Beatmap.Value = new DummyWorkingBeatmap(game);
- AddStep("load dummy beatmap", () => Add(loader = new PlayerLoader(() => new Player
+ AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player
{
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
})));
+ AddUntilStep(() => loader.IsCurrentScreen(), "wait for current");
+
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
- AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
+ AddUntilStep(() => !loader.IsCurrentScreen(), "wait for no longer current");
+
+ AddStep("exit loader", () => loader.Exit());
+
+ AddUntilStep(() => !loader.IsAlive, "wait for no longer alive");
AddStep("load slow dummy beatmap", () =>
{
SlowLoadPlayer slow = null;
- Add(loader = new PlayerLoader(() => slow = new SlowLoadPlayer
+ stack.Push(loader = new PlayerLoader(() => slow = new SlowLoadPlayer
{
AllowPause = false,
AllowLeadIn = false,
@@ -42,7 +61,7 @@ namespace osu.Game.Tests.Visual
Scheduler.AddDelayed(() => slow.Ready = true, 5000);
});
- AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
+ AddUntilStep(() => !loader.IsCurrentScreen(), "wait for no longer current");
}
protected class SlowLoadPlayer : Player
diff --git a/osu.Game.Tests/Visual/TestCasePollingComponent.cs b/osu.Game.Tests/Visual/TestCasePollingComponent.cs
index 68c44c7758..63f4f88948 100644
--- a/osu.Game.Tests/Visual/TestCasePollingComponent.cs
+++ b/osu.Game.Tests/Visual/TestCasePollingComponent.cs
@@ -56,6 +56,7 @@ namespace osu.Game.Tests.Visual
});
[Test]
+ [Ignore("polling is threaded, and it's very hard to hook into it correctly")]
public void TestInstantPolling()
{
createPoller(true);
@@ -106,8 +107,11 @@ namespace osu.Game.Tests.Visual
private void checkCount(int checkValue)
{
- Logger.Log($"value is {count}");
- AddAssert($"count is {checkValue}", () => count == checkValue);
+ AddAssert($"count is {checkValue}", () =>
+ {
+ Logger.Log($"value is {count}");
+ return count == checkValue;
+ });
}
private void createPoller(bool instant) => AddStep("create poller", () =>
diff --git a/osu.Game.Tests/Visual/TestCaseResults.cs b/osu.Game.Tests/Visual/TestCaseResults.cs
index 403742a7b5..c2880c1ea2 100644
--- a/osu.Game.Tests/Visual/TestCaseResults.cs
+++ b/osu.Game.Tests/Visual/TestCaseResults.cs
@@ -16,7 +16,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
- public class TestCaseResults : OsuTestCase
+ public class TestCaseResults : ScreenTestCase
{
private BeatmapManager beatmaps;
@@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
- Add(new SoloResults(new ScoreInfo
+ LoadScreen(new SoloResults(new ScoreInfo
{
TotalScore = 2845370,
Accuracy = 0.98,
diff --git a/osu.Game.Tests/Visual/TestCaseScoreCounter.cs b/osu.Game.Tests/Visual/TestCaseScoreCounter.cs
index e4e80e4017..3519ea67a6 100644
--- a/osu.Game.Tests/Visual/TestCaseScoreCounter.cs
+++ b/osu.Game.Tests/Visual/TestCaseScoreCounter.cs
@@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual
AddStep(@"Hit! :D", delegate
{
- score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0);
+ score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current.Value > 0 ? comboCounter.Current.Value - 1 : 0) / 25.0);
comboCounter.Increment();
numerator++;
denominator++;
diff --git a/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs b/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs
index 0f34e4f10a..204f4a493d 100644
--- a/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs
+++ b/osu.Game.Tests/Visual/TestCaseScreenBreadcrumbControl.cs
@@ -19,16 +19,18 @@ namespace osu.Game.Tests.Visual
public class TestCaseScreenBreadcrumbControl : OsuTestCase
{
private readonly ScreenBreadcrumbControl breadcrumbs;
- private Screen currentScreen, changedScreen;
+ private readonly ScreenStack screenStack;
public TestCaseScreenBreadcrumbControl()
{
- TestScreen startScreen;
OsuSpriteText titleText;
+ IScreen startScreen = new TestScreenOne();
+ screenStack = new ScreenStack(startScreen) { RelativeSizeAxes = Axes.Both };
+
Children = new Drawable[]
{
- currentScreen = startScreen = new TestScreenOne(),
+ screenStack,
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
@@ -37,7 +39,7 @@ namespace osu.Game.Tests.Visual
Spacing = new Vector2(10),
Children = new Drawable[]
{
- breadcrumbs = new ScreenBreadcrumbControl(startScreen)
+ breadcrumbs = new ScreenBreadcrumbControl(screenStack)
{
RelativeSizeAxes = Axes.X,
},
@@ -46,12 +48,7 @@ namespace osu.Game.Tests.Visual
},
};
- breadcrumbs.Current.ValueChanged += s =>
- {
- titleText.Text = $"Changed to {s.ToString()}";
- changedScreen = s;
- };
-
+ breadcrumbs.Current.ValueChanged += screen => titleText.Text = $"Changed to {screen.NewValue.ToString()}";
breadcrumbs.Current.TriggerChange();
waitForCurrent();
@@ -60,18 +57,14 @@ namespace osu.Game.Tests.Visual
pushNext();
waitForCurrent();
- AddStep(@"make start current", () =>
- {
- startScreen.MakeCurrent();
- currentScreen = startScreen;
- });
+ AddStep(@"make start current", () => startScreen.MakeCurrent());
waitForCurrent();
pushNext();
waitForCurrent();
AddAssert(@"only 2 items", () => breadcrumbs.Items.Count() == 2);
- AddStep(@"exit current", () => changedScreen.Exit());
- AddAssert(@"current screen is first", () => startScreen == changedScreen);
+ AddStep(@"exit current", () => screenStack.CurrentScreen.Exit());
+ AddAssert(@"current screen is first", () => startScreen == screenStack.CurrentScreen);
}
[BackgroundDependencyLoader]
@@ -80,8 +73,8 @@ namespace osu.Game.Tests.Visual
breadcrumbs.StripColour = colours.Blue;
}
- private void pushNext() => AddStep(@"push next screen", () => currentScreen = ((TestScreen)currentScreen).PushNext());
- private void waitForCurrent() => AddUntilStep(() => currentScreen.IsCurrentScreen, "current screen");
+ private void pushNext() => AddStep(@"push next screen", () => ((TestScreen)screenStack.CurrentScreen).PushNext());
+ private void waitForCurrent() => AddUntilStep(() => screenStack.CurrentScreen.IsCurrentScreen(), "current screen");
private abstract class TestScreen : OsuScreen
{
@@ -91,14 +84,14 @@ namespace osu.Game.Tests.Visual
public TestScreen PushNext()
{
TestScreen screen = CreateNextScreen();
- Push(screen);
+ this.Push(screen);
return screen;
}
protected TestScreen()
{
- Child = new FillFlowContainer
+ InternalChild = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Tests/Visual/TestCaseSkinReloadable.cs b/osu.Game.Tests/Visual/TestCaseSkinReloadable.cs
new file mode 100644
index 0000000000..94f01e9d32
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseSkinReloadable.cs
@@ -0,0 +1,153 @@
+// 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 NUnit.Framework;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Graphics;
+using osu.Game.Skinning;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual
+{
+ public class TestCaseSkinReloadable : OsuTestCase
+ {
+ [Test]
+ public void TestInitialLoad()
+ {
+ var secondarySource = new SecondarySource();
+ SkinConsumer consumer = null;
+
+ AddStep("setup layout", () =>
+ {
+ Child = new SkinSourceContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new LocalSkinOverrideContainer(secondarySource)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)
+ }
+ };
+ });
+
+ AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
+ AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
+ }
+
+ [Test]
+ public void TestOverride()
+ {
+ var secondarySource = new SecondarySource();
+
+ SkinConsumer consumer = null;
+ Container target = null;
+
+ AddStep("setup layout", () =>
+ {
+ Child = new SkinSourceContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = target = new LocalSkinOverrideContainer(secondarySource)
+ {
+ RelativeSizeAxes = Axes.Both,
+ }
+ };
+ });
+
+ AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)));
+ AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
+ AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
+ }
+
+ private class NamedBox : Container
+ {
+ public NamedBox(string name)
+ {
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.Black,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new SpriteText
+ {
+ Font = OsuFont.Default.With(size: 40),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = name
+ }
+ };
+ }
+ }
+
+ private class SkinConsumer : SkinnableDrawable
+ {
+ public new Drawable Drawable => base.Drawable;
+ public int SkinChangedCount { get; private set; }
+
+ public SkinConsumer(string name, Func defaultImplementation, Func allowFallback = null, bool restrictSize = true)
+ : base(name, defaultImplementation, allowFallback, restrictSize)
+ {
+ }
+
+ protected override void SkinChanged(ISkinSource skin, bool allowFallback)
+ {
+ base.SkinChanged(skin, allowFallback);
+ SkinChangedCount++;
+ }
+ }
+
+ private class BaseSourceBox : NamedBox
+ {
+ public BaseSourceBox()
+ : base("Base Source")
+ {
+ }
+ }
+
+ private class SecondarySourceBox : NamedBox
+ {
+ public SecondarySourceBox()
+ : base("Secondary Source")
+ {
+ }
+ }
+
+ private class SecondarySource : ISkinSource
+ {
+ public event Action SourceChanged;
+
+ public void TriggerSourceChanged() => SourceChanged?.Invoke();
+
+ public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox();
+
+ public Texture GetTexture(string componentName) => throw new NotImplementedException();
+
+ public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
+
+ public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
+ }
+
+ private class SkinSourceContainer : Container, ISkinSource
+ {
+ public event Action SourceChanged;
+
+ public void TriggerSourceChanged() => SourceChanged?.Invoke();
+
+ public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox();
+
+ public Texture GetTexture(string componentName) => throw new NotImplementedException();
+
+ public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
+
+ public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseSongProgress.cs b/osu.Game.Tests/Visual/TestCaseSongProgress.cs
index 9ce33f21d6..cdb1cd2286 100644
--- a/osu.Game.Tests/Visual/TestCaseSongProgress.cs
+++ b/osu.Game.Tests/Visual/TestCaseSongProgress.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
@@ -15,23 +16,29 @@ namespace osu.Game.Tests.Visual
public class TestCaseSongProgress : OsuTestCase
{
private readonly SongProgress progress;
- private readonly SongProgressGraph graph;
+ private readonly TestSongProgressGraph graph;
private readonly StopwatchClock clock;
+ [Cached]
+ private readonly GameplayClock gameplayClock;
+
+ private readonly FramedClock framedClock;
+
public TestCaseSongProgress()
{
clock = new StopwatchClock(true);
+ gameplayClock = new GameplayClock(framedClock = new FramedClock(clock));
+
Add(progress = new SongProgress
{
RelativeSizeAxes = Axes.X,
- AudioClock = new StopwatchClock(true),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
});
- Add(graph = new SongProgressGraph
+ Add(graph = new TestSongProgressGraph
{
RelativeSizeAxes = Axes.X,
Height = 200,
@@ -39,13 +46,24 @@ namespace osu.Game.Tests.Visual
Origin = Anchor.TopLeft,
});
+ AddWaitStep(5);
+ AddAssert("ensure not created", () => graph.CreationCount == 0);
+
+ AddStep("display values", displayNewValues);
+ AddWaitStep(5);
+ AddUntilStep(() => graph.CreationCount == 1, "wait for creation count");
+
AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking);
AddWaitStep(5);
+ AddUntilStep(() => graph.CreationCount == 1, "wait for creation count");
+
AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking);
- AddWaitStep(2);
+ AddWaitStep(5);
+ AddUntilStep(() => graph.CreationCount == 1, "wait for creation count");
AddRepeatStep("New Values", displayNewValues, 5);
- displayNewValues();
+ AddWaitStep(5);
+ AddAssert("ensure debounced", () => graph.CreationCount == 2);
}
private void displayNewValues()
@@ -57,8 +75,24 @@ namespace osu.Game.Tests.Visual
progress.Objects = objects;
graph.Objects = objects;
- progress.AudioClock = clock;
- progress.OnSeek = pos => clock.Seek(pos);
+ progress.RequestSeek = pos => clock.Seek(pos);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+ framedClock.ProcessFrame();
+ }
+
+ private class TestSongProgressGraph : SongProgressGraph
+ {
+ public int CreationCount { get; private set; }
+
+ protected override void RecreateGraph()
+ {
+ base.RecreateGraph();
+ CreationCount++;
+ }
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseStoryboard.cs b/osu.Game.Tests/Visual/TestCaseStoryboard.cs
index a4ad213116..c4b41e40f4 100644
--- a/osu.Game.Tests/Visual/TestCaseStoryboard.cs
+++ b/osu.Game.Tests/Visual/TestCaseStoryboard.cs
@@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -49,7 +50,10 @@ namespace osu.Game.Tests.Visual
});
AddStep("Restart", restart);
- AddToggleStep("Passing", passing => { if (storyboard != null) storyboard.Passing = passing; });
+ AddToggleStep("Passing", passing =>
+ {
+ if (storyboard != null) storyboard.Passing = passing;
+ });
}
[BackgroundDependencyLoader]
@@ -58,15 +62,15 @@ namespace osu.Game.Tests.Visual
Beatmap.ValueChanged += beatmapChanged;
}
- private void beatmapChanged(WorkingBeatmap working)
- => loadStoryboard(working);
+ private void beatmapChanged(ValueChangedEvent e)
+ => loadStoryboard(e.NewValue);
private void restart()
{
var track = Beatmap.Value.Track;
track.Reset();
- loadStoryboard(Beatmap);
+ loadStoryboard(Beatmap.Value);
track.Start();
}
@@ -78,7 +82,7 @@ namespace osu.Game.Tests.Visual
var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
storyboardContainer.Clock = decoupledClock;
- storyboard = working.Storyboard.CreateDrawable(Beatmap);
+ storyboard = working.Storyboard.CreateDrawable(Beatmap.Value);
storyboard.Passing = false;
storyboardContainer.Add(storyboard);
diff --git a/osu.Game.Tests/Visual/TestCaseTabControl.cs b/osu.Game.Tests/Visual/TestCaseTabControl.cs
index 82f56cb0f8..ebf8f3bb30 100644
--- a/osu.Game.Tests/Visual/TestCaseTabControl.cs
+++ b/osu.Game.Tests/Visual/TestCaseTabControl.cs
@@ -33,9 +33,9 @@ namespace osu.Game.Tests.Visual
filter.PinItem(GroupMode.All);
filter.PinItem(GroupMode.RecentlyPlayed);
- filter.Current.ValueChanged += newFilter =>
+ filter.Current.ValueChanged += grouping =>
{
- text.Text = "Currently Selected: " + newFilter.ToString();
+ text.Text = "Currently Selected: " + grouping.NewValue.ToString();
};
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/TestCaseUpdateableBeatmapBackgroundSprite.cs
index 14f178a293..3ee617e092 100644
--- a/osu.Game.Tests/Visual/TestCaseUpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game.Tests/Visual/TestCaseUpdateableBeatmapBackgroundSprite.cs
@@ -3,7 +3,7 @@
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
diff --git a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs
index 6f164890f9..49ffa2d63f 100644
--- a/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs
+++ b/osu.Game.Tests/Visual/TestCaseUserProfileHeader.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual
typeof(ProfileHeader),
typeof(RankGraph),
typeof(LineGraph),
- typeof(SupporterIcon)
+ typeof(ProfileHeaderTabControl),
};
[Resolved]
diff --git a/osu.Game.Tests/Visual/TestCaseWaveContainer.cs b/osu.Game.Tests/Visual/TestCaseWaveContainer.cs
index 56dcfc3cbb..07a282a1a7 100644
--- a/osu.Game.Tests/Visual/TestCaseWaveContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseWaveContainer.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- TextSize = 20,
+ Font = OsuFont.GetFont(size: 20),
Text = @"Wave Container",
},
},
diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs
index 72db4b0c17..2028671b0e 100644
--- a/osu.Game.Tests/WaveformTestBeatmap.cs
+++ b/osu.Game.Tests/WaveformTestBeatmap.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO.Archives;
-using osu.Game.Tests.Beatmaps.IO;
+using osu.Game.Tests.Resources;
namespace osu.Game.Tests
{
@@ -18,12 +18,12 @@ namespace osu.Game.Tests
public class WaveformTestBeatmap : WorkingBeatmap
{
private readonly ZipArchiveReader reader;
- private readonly FileStream stream;
+ private readonly Stream stream;
public WaveformTestBeatmap()
: base(new BeatmapInfo())
{
- stream = File.OpenRead(ImportBeatmapTest.TEST_OSZ_PATH);
+ stream = TestResources.GetTestBeatmapStream();
reader = new ZipArchiveReader(stream);
}
diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs
index 5abf6bdb08..3b21bdefc4 100644
--- a/osu.Game/Audio/PreviewTrack.cs
+++ b/osu.Game/Audio/PreviewTrack.cs
@@ -28,6 +28,7 @@ namespace osu.Game.Audio
private void load()
{
track = GetTrack();
+ track.Completed += () => Schedule(Stop);
}
///
@@ -50,15 +51,6 @@ namespace osu.Game.Audio
///
public bool IsRunning => track?.IsRunning ?? false;
- protected override void Update()
- {
- base.Update();
-
- // Todo: Track currently doesn't signal its completion, so we have to handle it manually
- if (hasStarted && track.HasCompleted)
- Stop();
- }
-
private ScheduledDelegate startDelegate;
///
@@ -71,6 +63,7 @@ namespace osu.Game.Audio
if (hasStarted)
return;
+
hasStarted = true;
track.Restart();
@@ -89,6 +82,7 @@ namespace osu.Game.Audio
if (!hasStarted)
return;
+
hasStarted = false;
track.Stop();
diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs
index 6de7d0e4f7..99c0d70ac9 100644
--- a/osu.Game/Audio/PreviewTrackManager.cs
+++ b/osu.Game/Audio/PreviewTrackManager.cs
@@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.IO.Stores;
diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs
index 736caf69e8..5bc6dce60b 100644
--- a/osu.Game/Audio/SampleInfo.cs
+++ b/osu.Game/Audio/SampleInfo.cs
@@ -50,12 +50,14 @@ namespace osu.Game.Audio
{
if (!string.IsNullOrEmpty(Suffix))
yield return $"{Namespace}/{Bank}-{Name}{Suffix}";
+
yield return $"{Namespace}/{Bank}-{Name}";
}
// check non-namespace as a fallback even when we have a namespace
if (!string.IsNullOrEmpty(Suffix))
yield return $"{Bank}-{Name}{Suffix}";
+
yield return $"{Bank}-{Name}";
}
}
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index d6041ad38d..b6fa6674f6 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -16,6 +16,7 @@ namespace osu.Game.Beatmaps
where T : HitObject
{
private event Action> ObjectConverted;
+
event Action> IBeatmapConverter.ObjectConverted
{
add => ObjectConverted += value;
diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs
index 21b943e111..8727431e0e 100644
--- a/osu.Game/Beatmaps/BeatmapDifficulty.cs
+++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs
@@ -48,6 +48,7 @@ namespace osu.Game.Beatmaps
return mid + (max - mid) * (difficulty - 5) / 5;
if (difficulty < 5)
return mid - (mid - min) * (5 - difficulty) / 5;
+
return mid;
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 42048692fc..88f5e777e3 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -11,7 +11,6 @@ using Microsoft.EntityFrameworkCore;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions;
-using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Framework.Platform;
@@ -50,11 +49,6 @@ namespace osu.Game.Beatmaps
///
public event Action BeatmapDownloadFailed;
- ///
- /// Fired when a beatmap load is requested (into the interactive game UI).
- ///
- public Action PresentBeatmap;
-
///
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
///
@@ -151,8 +145,7 @@ namespace osu.Game.Beatmaps
var downloadNotification = new DownloadNotification
{
- CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!",
- Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
+ Text = $"Downloading {beatmapSetInfo}",
};
var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);
@@ -163,26 +156,12 @@ namespace osu.Game.Beatmaps
downloadNotification.Progress = progress;
};
- request.Success += data =>
+ request.Success += filename =>
{
- downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}";
-
Task.Factory.StartNew(() =>
{
- BeatmapSetInfo importedBeatmap;
-
// This gets scheduled back to the update thread, but we want the import to run in the background.
- using (var stream = new MemoryStream(data))
- using (var archive = new ZipArchiveReader(stream, beatmapSetInfo.ToString()))
- importedBeatmap = Import(archive);
-
- downloadNotification.CompletionClickAction = () =>
- {
- PresentCompletedImport(importedBeatmap.Yield());
- return true;
- };
- downloadNotification.State = ProgressNotificationState.Completed;
-
+ Import(downloadNotification, filename);
currentDownloads.Remove(request);
}, TaskCreationOptions.LongRunning);
};
@@ -210,17 +189,21 @@ namespace osu.Game.Beatmaps
PostNotification?.Invoke(downloadNotification);
// don't run in the main api queue as this is a long-running task.
- Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
+ Task.Factory.StartNew(() =>
+ {
+ try
+ {
+ request.Perform(api);
+ }
+ catch (Exception e)
+ {
+ // no need to handle here as exceptions will filter down to request.Failure above.
+ }
+ }, TaskCreationOptions.LongRunning);
BeatmapDownloadBegan?.Invoke(request);
return true;
}
- protected override void PresentCompletedImport(IEnumerable imported)
- {
- base.PresentCompletedImport(imported);
- PresentBeatmap?.Invoke(imported.LastOrDefault());
- }
-
///
/// Get an existing download request if it exists.
///
diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs
index bac8ad5ed7..001f319307 100644
--- a/osu.Game/Beatmaps/BeatmapMetadata.cs
+++ b/osu.Game/Beatmaps/BeatmapMetadata.cs
@@ -34,8 +34,8 @@ namespace osu.Game.Beatmaps
[Column("Author")]
public string AuthorString
{
- get { return Author?.Username; }
- set { Author = new User { Username = value }; }
+ get => Author?.Username;
+ set => Author = new User { Username = value };
}
///
@@ -48,6 +48,7 @@ namespace osu.Game.Beatmaps
[JsonProperty(@"tags")]
public string Tags { get; set; }
+
public int PreviewTime { get; set; }
public string AudioFile { get; set; }
public string BackgroundFile { get; set; }
@@ -72,15 +73,15 @@ namespace osu.Game.Beatmaps
return false;
return Title == other.Title
- && TitleUnicode == other.TitleUnicode
- && Artist == other.Artist
- && ArtistUnicode == other.ArtistUnicode
- && AuthorString == other.AuthorString
- && Source == other.Source
- && Tags == other.Tags
- && PreviewTime == other.PreviewTime
- && AudioFile == other.AudioFile
- && BackgroundFile == other.BackgroundFile;
+ && TitleUnicode == other.TitleUnicode
+ && Artist == other.Artist
+ && ArtistUnicode == other.ArtistUnicode
+ && AuthorString == other.AuthorString
+ && Source == other.Source
+ && Tags == other.Tags
+ && PreviewTime == other.PreviewTime
+ && AudioFile == other.AudioFile
+ && BackgroundFile == other.BackgroundFile;
}
}
}
diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs
index ad7648e7fd..f4b7b1d74f 100644
--- a/osu.Game/Beatmaps/BeatmapStore.cs
+++ b/osu.Game/Beatmaps/BeatmapStore.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps
///
/// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing
///
- public class BeatmapStore : MutableDatabaseBackedStore
+ public class BeatmapStore : MutableDatabaseBackedStoreWithFileIncludes
{
public event Action BeatmapHidden;
public event Action BeatmapRestored;
@@ -34,6 +34,7 @@ namespace osu.Game.Beatmaps
Refresh(ref beatmap, Beatmaps);
if (beatmap.Hidden) return false;
+
beatmap.Hidden = true;
}
@@ -53,6 +54,7 @@ namespace osu.Game.Beatmaps
Refresh(ref beatmap, Beatmaps);
if (!beatmap.Hidden) return false;
+
beatmap.Hidden = false;
}
@@ -62,18 +64,17 @@ namespace osu.Game.Beatmaps
protected override IQueryable AddIncludesForDeletion(IQueryable query) =>
base.AddIncludesForDeletion(query)
- .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
- .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Metadata)
- .Include(s => s.Beatmaps).ThenInclude(b => b.Scores);
+ .Include(s => s.Beatmaps).ThenInclude(b => b.Scores)
+ .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
+ .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata);
protected override IQueryable AddIncludesForConsumption(IQueryable query) =>
base.AddIncludesForConsumption(query)
.Include(s => s.Metadata)
.Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
- .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
- .Include(s => s.Files).ThenInclude(f => f.FileInfo);
+ .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata);
protected override void Purge(List items, OsuDbContext context)
{
diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs
index bbd0fbfb06..657dc06297 100644
--- a/osu.Game/Beatmaps/BindableBeatmap.cs
+++ b/osu.Game/Beatmaps/BindableBeatmap.cs
@@ -6,15 +6,15 @@ using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
namespace osu.Game.Beatmaps
{
///
- /// A for the beatmap.
- /// This should be used sparingly in-favour of .
+ /// A for the beatmap.
+ /// This should be used sparingly in-favour of .
///
- public abstract class BindableBeatmap : NonNullableBindable, IBindableBeatmap
+ public abstract class BindableBeatmap : NonNullableBindable
{
private AudioManager audioManager;
private WorkingBeatmap lastBeatmap;
@@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps
this.audioManager = audioManager;
- ValueChanged += registerAudioTrack;
+ ValueChanged += b => registerAudioTrack(b.NewValue);
// If the track has changed prior to this being called, let's register it
if (Value != Default)
@@ -62,9 +62,6 @@ namespace osu.Game.Beatmaps
lastBeatmap = beatmap;
}
- [NotNull]
- IBindableBeatmap IBindableBeatmap.GetBoundCopy() => GetBoundCopy();
-
///
/// Retrieve a new instance weakly bound to this .
/// If you are further binding to events of the retrieved , ensure a local reference is held.
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs
index c8c735b439..0c59eec1ef 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs
@@ -13,8 +13,8 @@ namespace osu.Game.Beatmaps.Drawables
public BeatmapBackgroundSprite(WorkingBeatmap working)
{
- if (working == null)
- throw new ArgumentNullException(nameof(working));
+ if (working == null)
+ throw new ArgumentNullException(nameof(working));
this.working = working;
}
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs
deleted file mode 100644
index 4b3ee04eca..0000000000
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs
+++ /dev/null
@@ -1,112 +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.Linq;
-using osu.Framework.Allocation;
-using osu.Framework.Configuration;
-using osu.Framework.Graphics;
-using osu.Game.Online.API.Requests;
-
-namespace osu.Game.Beatmaps.Drawables
-{
- ///
- /// A component to allow downloading of a beatmap set. Automatically handles state syncing between other instances.
- ///
- public class BeatmapSetDownloader : Component
- {
- private readonly BeatmapSetInfo set;
- private readonly bool noVideo;
-
- private BeatmapManager beatmaps;
-
- ///
- /// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
- ///
- public readonly Bindable DownloadState = new Bindable();
-
- public BeatmapSetDownloader(BeatmapSetInfo set, bool noVideo = false)
- {
- this.set = set;
- this.noVideo = noVideo;
- }
-
- [BackgroundDependencyLoader]
- private void load(BeatmapManager beatmaps)
- {
- this.beatmaps = beatmaps;
-
- beatmaps.ItemAdded += setAdded;
- beatmaps.ItemRemoved += setRemoved;
- beatmaps.BeatmapDownloadBegan += downloadBegan;
- beatmaps.BeatmapDownloadFailed += downloadFailed;
-
- // initial value
- if (set.OnlineBeatmapSetID != null && beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any())
- DownloadState.Value = DownloadStatus.Downloaded;
- else if (beatmaps.GetExistingDownload(set) != null)
- DownloadState.Value = DownloadStatus.Downloading;
- else
- DownloadState.Value = DownloadStatus.NotDownloaded;
- }
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
-
- if (beatmaps != null)
- {
- beatmaps.ItemAdded -= setAdded;
- beatmaps.ItemRemoved -= setRemoved;
- beatmaps.BeatmapDownloadBegan -= downloadBegan;
- beatmaps.BeatmapDownloadFailed -= downloadFailed;
- }
- }
-
- ///
- /// Begin downloading the associated beatmap set.
- ///
- /// True if downloading began. False if an existing download is active or completed.
- public void Download()
- {
- if (DownloadState.Value > DownloadStatus.NotDownloaded)
- return;
-
- if (beatmaps.Download(set, noVideo))
- {
- // Only change state if download can happen
- DownloadState.Value = DownloadStatus.Downloading;
- }
- }
-
- private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => Schedule(() =>
- {
- if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
- DownloadState.Value = DownloadStatus.Downloaded;
- });
-
- private void setRemoved(BeatmapSetInfo s) => Schedule(() =>
- {
- if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
- DownloadState.Value = DownloadStatus.NotDownloaded;
- });
-
- private void downloadBegan(DownloadBeatmapSetRequest d)
- {
- if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
- DownloadState.Value = DownloadStatus.Downloading;
- }
-
- private void downloadFailed(DownloadBeatmapSetRequest d)
- {
- if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
- DownloadState.Value = DownloadStatus.NotDownloaded;
- }
-
- public enum DownloadStatus
- {
- NotDownloaded,
- Downloading,
- Downloaded,
- }
- }
-}
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
index 5e20ca8bc8..351e5df17a 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
@@ -4,6 +4,7 @@
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.Graphics;
@@ -22,6 +23,7 @@ namespace osu.Game.Beatmaps.Drawables
{
if (status == value)
return;
+
status = value;
Alpha = value == BeatmapSetOnlineStatus.None ? 0 : 1;
@@ -31,8 +33,8 @@ namespace osu.Game.Beatmaps.Drawables
public float TextSize
{
- get => statusText.TextSize;
- set => statusText.TextSize = value;
+ get => statusText.Font.Size;
+ set => statusText.Font = statusText.Font.With(size: value);
}
public MarginPadding TextPadding
@@ -58,7 +60,7 @@ namespace osu.Game.Beatmaps.Drawables
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(weight: FontWeight.Bold)
},
};
diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs
index b025b5985c..f1607ad749 100644
--- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs
+++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs
@@ -48,11 +48,12 @@ namespace osu.Game.Beatmaps.Drawables
var rating = beatmap.StarDifficulty;
- if (rating < 1.5) return DifficultyRating.Easy;
- if (rating < 2.25) return DifficultyRating.Normal;
- if (rating < 3.75) return DifficultyRating.Hard;
- if (rating < 5.25) return DifficultyRating.Insane;
- if (rating < 6.75) return DifficultyRating.Expert;
+ if (rating < 2.0) return DifficultyRating.Easy;
+ if (rating < 2.7) return DifficultyRating.Normal;
+ if (rating < 4.0) return DifficultyRating.Hard;
+ if (rating < 5.3) return DifficultyRating.Insane;
+ if (rating < 6.5) return DifficultyRating.Expert;
+
return DifficultyRating.ExpertPlus;
}
diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
index c66052052f..012926fb0e 100644
--- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -13,27 +13,39 @@ namespace osu.Game.Beatmaps.Drawables
///
public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable
{
- public readonly IBindable Beatmap = new Bindable();
+ public readonly Bindable Beatmap = new Bindable();
[Resolved]
private BeatmapManager beatmaps { get; set; }
- public UpdateableBeatmapBackgroundSprite()
+ private readonly BeatmapSetCoverType beatmapSetCoverType;
+
+ public UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover)
{
- Beatmap.BindValueChanged(b => Model = b);
+ Beatmap.BindValueChanged(b => Model = b.NewValue);
+ this.beatmapSetCoverType = beatmapSetCoverType;
}
protected override Drawable CreateDrawable(BeatmapInfo model)
{
- return new DelayedLoadUnloadWrapper(() => {
+ return new DelayedLoadUnloadWrapper(() =>
+ {
Drawable drawable;
var localBeatmap = beatmaps.GetWorkingBeatmap(model);
- if (localBeatmap.BeatmapInfo.ID == 0 && model?.BeatmapSet?.OnlineInfo != null)
- drawable = new BeatmapSetCover(model.BeatmapSet);
- else
+ if (model?.BeatmapSet?.OnlineInfo != null)
+ drawable = new BeatmapSetCover(model.BeatmapSet, beatmapSetCoverType);
+ else if (localBeatmap.BeatmapInfo.ID != 0)
+ {
+ // Fall back to local background if one exists
drawable = new BeatmapBackgroundSprite(localBeatmap);
+ }
+ else
+ {
+ // Use the default background if somehow an online set does not exist and we don't have a local copy.
+ drawable = new BeatmapBackgroundSprite(beatmaps.DefaultBeatmap);
+ }
drawable.RelativeSizeAxes = Axes.Both;
drawable.Anchor = Anchor.Centre;
diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs
index 45df2b3406..367b63d6d1 100644
--- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs
+++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs
@@ -13,12 +13,14 @@ namespace osu.Game.Beatmaps.Drawables
private Drawable displayedCover;
private BeatmapSetInfo beatmapSet;
+
public BeatmapSetInfo BeatmapSet
{
- get { return beatmapSet; }
+ get => beatmapSet;
set
{
if (value == beatmapSet) return;
+
beatmapSet = value;
if (IsLoaded)
@@ -27,12 +29,14 @@ namespace osu.Game.Beatmaps.Drawables
}
private BeatmapSetCoverType coverType = BeatmapSetCoverType.Cover;
+
public BeatmapSetCoverType CoverType
{
- get { return coverType; }
+ get => coverType;
set
{
if (value == coverType) return;
+
coverType = value;
if (IsLoaded)
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 34e5afd1cd..040f582e3b 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -72,6 +72,7 @@ namespace osu.Game.Beatmaps.Formats
var index = line.AsSpan().IndexOf("//".AsSpan());
if (index > 0)
return line.Substring(0, index);
+
return line;
}
@@ -115,6 +116,7 @@ namespace osu.Game.Beatmaps.Formats
else
{
if (!(output is IHasCustomColours tHasCustomColours)) return;
+
tHasCustomColours.CustomColours[pair.Key] = colour;
}
}
diff --git a/osu.Game/Beatmaps/IBindableBeatmap.cs b/osu.Game/Beatmaps/IBindableBeatmap.cs
deleted file mode 100644
index 22b7a5a956..0000000000
--- a/osu.Game/Beatmaps/IBindableBeatmap.cs
+++ /dev/null
@@ -1,19 +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.Configuration;
-
-namespace osu.Game.Beatmaps
-{
- ///
- /// Read-only interface for the beatmap.
- ///
- public interface IBindableBeatmap : IBindable
- {
- ///
- /// Retrieve a new instance weakly bound to this .
- /// If you are further binding to events of the retrieved , ensure a local reference is held.
- ///
- IBindableBeatmap GetBoundCopy();
- }
-}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 18ff97e079..2e36d87024 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -2,16 +2,16 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Audio.Track;
-using osu.Framework.Configuration;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
-using System.Linq;
using osu.Game.Storyboards;
using osu.Framework.IO.File;
using System.IO;
+using System.Linq;
using System.Threading;
+using osu.Framework.Bindables;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
@@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps
BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
- Mods.ValueChanged += mods => applyRateAdjustments();
+ Mods.ValueChanged += _ => applyRateAdjustments();
beatmap = new RecyclableLazy(() =>
{
diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs
index e218f31f83..f547a7d3e1 100644
--- a/osu.Game/Configuration/DatabasedConfigManager.cs
+++ b/osu.Game/Configuration/DatabasedConfigManager.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Game.Rulesets;
@@ -60,9 +61,9 @@ namespace osu.Game.Configuration
databasedSettings.Add(setting);
}
- bindable.ValueChanged += v =>
+ bindable.ValueChanged += b =>
{
- setting.Value = v;
+ setting.Value = b.NewValue;
settings.Update(setting);
};
}
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 613efe1801..2d8cfa12ee 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -33,14 +33,14 @@ namespace osu.Game.Configuration
Set(OsuSetting.Username, string.Empty);
Set(OsuSetting.Token, string.Empty);
- Set(OsuSetting.SavePassword, false).ValueChanged += val =>
+ Set(OsuSetting.SavePassword, false).ValueChanged += enabled =>
{
- if (val) Set(OsuSetting.SaveUsername, true);
+ if (enabled.NewValue) Set(OsuSetting.SaveUsername, true);
};
- Set(OsuSetting.SaveUsername, true).ValueChanged += val =>
+ Set(OsuSetting.SaveUsername, true).ValueChanged += enabled =>
{
- if (!val) Set(OsuSetting.SavePassword, false);
+ if (!enabled.NewValue) Set(OsuSetting.SavePassword, false);
};
Set(OsuSetting.ExternalLinkWarning, true);
@@ -72,9 +72,6 @@ namespace osu.Game.Configuration
Set(OsuSetting.MenuParallax, true);
- Set(OsuSetting.SnakingInSliders, true);
- Set(OsuSetting.SnakingOutSliders, true);
-
// Gameplay
Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
@@ -150,8 +147,6 @@ namespace osu.Game.Configuration
DisplayStarsMinimum,
DisplayStarsMaximum,
RandomSelectAlgorithm,
- SnakingInSliders,
- SnakingOutSliders,
ShowFpsDisplay,
ChatDisplayHeight,
Version,
diff --git a/osu.Game/Configuration/RandomSelectAlgorithm.cs b/osu.Game/Configuration/RandomSelectAlgorithm.cs
index 35240db0ee..8d0c87374f 100644
--- a/osu.Game/Configuration/RandomSelectAlgorithm.cs
+++ b/osu.Game/Configuration/RandomSelectAlgorithm.cs
@@ -5,10 +5,11 @@ using System.ComponentModel;
namespace osu.Game.Configuration
{
- public enum RandomSelectAlgorithm
+ public enum RandomSelectAlgorithm
{
[Description("Never repeat")]
RandomPermutation,
+
[Description("Random")]
Random
}
diff --git a/osu.Game/Configuration/RankingType.cs b/osu.Game/Configuration/RankingType.cs
index 6514be750d..7701e1dd1d 100644
--- a/osu.Game/Configuration/RankingType.cs
+++ b/osu.Game/Configuration/RankingType.cs
@@ -8,8 +8,10 @@ namespace osu.Game.Configuration
public enum RankingType
{
Local,
+
[Description("Global")]
Top,
+
[Description("Selected Mods")]
SelectedMod,
Friends,
diff --git a/osu.Game/Configuration/ScalingMode.cs b/osu.Game/Configuration/ScalingMode.cs
index 1dadfcecc9..0bcc908f71 100644
--- a/osu.Game/Configuration/ScalingMode.cs
+++ b/osu.Game/Configuration/ScalingMode.cs
@@ -9,6 +9,7 @@ namespace osu.Game.Configuration
{
Off,
Everything,
+
[Description("Excluding overlays")]
ExcludeOverlays,
Gameplay,
diff --git a/osu.Game/Configuration/ScreenshotFormat.cs b/osu.Game/Configuration/ScreenshotFormat.cs
index 8015945747..6d4c96bfa9 100644
--- a/osu.Game/Configuration/ScreenshotFormat.cs
+++ b/osu.Game/Configuration/ScreenshotFormat.cs
@@ -9,6 +9,7 @@ namespace osu.Game.Configuration
{
[Description("JPG (web-friendly)")]
Jpg = 1,
+
[Description("PNG (lossless)")]
Png = 2
}
diff --git a/osu.Game/Configuration/ScrollVisualisationMethod.cs b/osu.Game/Configuration/ScrollVisualisationMethod.cs
index d2c09f9128..5f48fe8bfd 100644
--- a/osu.Game/Configuration/ScrollVisualisationMethod.cs
+++ b/osu.Game/Configuration/ScrollVisualisationMethod.cs
@@ -9,8 +9,10 @@ namespace osu.Game.Configuration
{
[Description("Sequential")]
Sequential,
+
[Description("Overlapping")]
Overlapping,
+
[Description("Constant")]
Constant
}
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 4b6662178f..9ec184abd7 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
+using osu.Framework;
using osu.Framework.Extensions;
using osu.Framework.IO.File;
using osu.Framework.Logging;
@@ -32,7 +33,7 @@ namespace osu.Game.Database
where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete
where TFileModel : INamedFileInfo, new()
{
- public delegate void ItemAddedDelegate(TModel model, bool existing, bool silent);
+ public delegate void ItemAddedDelegate(TModel model, bool existing);
///
/// Set an endpoint for notifications to be posted to.
@@ -53,6 +54,8 @@ namespace osu.Game.Database
public virtual string[] HandledExtensions => new[] { ".zip" };
+ public virtual bool SupportsImportFromStable => RuntimeInfo.IsDesktop;
+
protected readonly FileStore Files;
protected readonly IDatabaseContextFactory ContextFactory;
@@ -105,12 +108,12 @@ namespace osu.Game.Database
a.Invoke();
}
- protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStore modelStore, IIpcHost importHost = null)
+ protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null)
{
ContextFactory = contextFactory;
ModelStore = modelStore;
- ModelStore.ItemAdded += (item, silent) => handleEvent(() => ItemAdded?.Invoke(item, false, silent));
+ ModelStore.ItemAdded += item => handleEvent(() => ItemAdded?.Invoke(item, false));
ModelStore.ItemRemoved += s => handleEvent(() => ItemRemoved?.Invoke(s));
Files = new FileStore(contextFactory, storage);
@@ -128,14 +131,18 @@ namespace osu.Game.Database
/// One or more archive locations on disk.
public void Import(params string[] paths)
{
- var notification = new ProgressNotification
- {
- Text = "Import is initialising...",
- Progress = 0,
- State = ProgressNotificationState.Active,
- };
+ var notification = new ProgressNotification { State = ProgressNotificationState.Active };
PostNotification?.Invoke(notification);
+ Import(notification, paths);
+ }
+
+ protected void Import(ProgressNotification notification, params string[] paths)
+ {
+ notification.Progress = 0;
+ notification.Text = "Import is initialising...";
+
+ var term = $"{typeof(TModel).Name.Replace("Info", "").ToLower()}";
List imported = new List();
@@ -148,27 +155,22 @@ namespace osu.Game.Database
try
{
- notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}";
+ var text = "Importing ";
- TModel import;
- using (ArchiveReader reader = getReaderFrom(path))
- imported.Add(import = Import(reader));
+ if (path.Length > 1)
+ text += $"{++current} of {paths.Length} {term}s..";
+ else
+ text += $"{term}..";
+
+ // only show the filename if it isn't a temporary one (as those look ugly).
+ if (!path.Contains(Path.GetTempPath()))
+ text += $"\n{Path.GetFileName(path)}";
+
+ notification.Text = text;
+
+ imported.Add(Import(path));
notification.Progress = (float)current / paths.Length;
-
- // We may or may not want to delete the file depending on where it is stored.
- // e.g. reconstructing/repairing database with items from default storage.
- // Also, not always a single file, i.e. for LegacyFilesystemReader
- // TODO: Add a check to prevent files from storage to be deleted.
- try
- {
- if (import != null && File.Exists(path))
- File.Delete(path);
- }
- catch (Exception e)
- {
- Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})");
- }
}
catch (Exception e)
{
@@ -184,21 +186,57 @@ namespace osu.Game.Database
}
else
{
- notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!";
- notification.CompletionClickAction += () =>
+ notification.CompletionText = imported.Count == 1
+ ? $"Imported {imported.First()}!"
+ : $"Imported {current} {term}s!";
+
+ if (imported.Count > 0 && PresentImport != null)
{
- if (imported.Count > 0)
- PresentCompletedImport(imported);
- return true;
- };
+ notification.CompletionText += " Click to view.";
+ notification.CompletionClickAction = () =>
+ {
+ PresentImport?.Invoke(imported);
+ return true;
+ };
+ }
+
notification.State = ProgressNotificationState.Completed;
}
}
- protected virtual void PresentCompletedImport(IEnumerable imported)
+ ///
+ /// Import one from the filesystem and delete the file on success.
+ ///
+ /// The archive location on disk.
+ /// The imported model, if successful.
+ public TModel Import(string path)
{
+ TModel import;
+ using (ArchiveReader reader = getReaderFrom(path))
+ import = Import(reader);
+
+ // We may or may not want to delete the file depending on where it is stored.
+ // e.g. reconstructing/repairing database with items from default storage.
+ // Also, not always a single file, i.e. for LegacyFilesystemReader
+ // TODO: Add a check to prevent files from storage to be deleted.
+ try
+ {
+ if (import != null && File.Exists(path))
+ File.Delete(path);
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})");
+ }
+
+ return import;
}
+ ///
+ /// Fired when the user requests to view the resulting import.
+ ///
+ public Action> PresentImport;
+
///
/// Import an item from an .
///
@@ -213,7 +251,7 @@ namespace osu.Game.Database
model.Hash = computeHash(archive);
- return Import(model, false, archive);
+ return Import(model, archive);
}
catch (Exception e)
{
@@ -247,9 +285,8 @@ namespace osu.Game.Database
/// Import an item from a .
///
/// The model to be imported.
- /// Whether the user should be notified fo the import.
/// An optional archive to use for model population.
- public TModel Import(TModel item, bool silent = false, ArchiveReader archive = null)
+ public TModel Import(TModel item, ArchiveReader archive = null)
{
delayEvents();
@@ -269,7 +306,7 @@ namespace osu.Game.Database
{
Undelete(existing);
Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
- handleEvent(() => ItemAdded?.Invoke(existing, true, silent));
+ handleEvent(() => ItemAdded?.Invoke(existing, true));
return existing;
}
@@ -279,7 +316,7 @@ namespace osu.Game.Database
Populate(item, archive);
// import to store
- ModelStore.Add(item, silent);
+ ModelStore.Add(item);
}
catch (Exception e)
{
@@ -527,6 +564,7 @@ namespace osu.Game.Database
return new LegacyDirectoryArchiveReader(path);
if (File.Exists(path))
return new LegacyFileArchiveReader(path);
+
throw new InvalidFormatException($"{path} is not a valid archive");
}
}
diff --git a/osu.Game/Database/DatabaseWriteUsage.cs b/osu.Game/Database/DatabaseWriteUsage.cs
index 4659c212f3..1fd2f23d50 100644
--- a/osu.Game/Database/DatabaseWriteUsage.cs
+++ b/osu.Game/Database/DatabaseWriteUsage.cs
@@ -31,6 +31,7 @@ namespace osu.Game.Database
protected void Dispose(bool disposing)
{
if (isDisposed) return;
+
isDisposed = true;
try
diff --git a/osu.Game/Database/MutableDatabaseBackedStore.cs b/osu.Game/Database/MutableDatabaseBackedStore.cs
index 5e820d1478..39a48b5be6 100644
--- a/osu.Game/Database/MutableDatabaseBackedStore.cs
+++ b/osu.Game/Database/MutableDatabaseBackedStore.cs
@@ -16,9 +16,7 @@ namespace osu.Game.Database
public abstract class MutableDatabaseBackedStore : DatabaseBackedStore
where T : class, IHasPrimaryKey, ISoftDelete
{
- public delegate void ItemAddedDelegate(T model, bool silent);
-
- public event ItemAddedDelegate ItemAdded;
+ public event Action ItemAdded;
public event Action ItemRemoved;
protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null)
@@ -35,8 +33,7 @@ namespace osu.Game.Database
/// Add a to the database.
///
/// The item to add.
- /// Whether the user should be notified of the addition.
- public void Add(T item, bool silent)
+ public void Add(T item)
{
using (var usage = ContextFactory.GetForWrite())
{
@@ -44,7 +41,7 @@ namespace osu.Game.Database
context.Attach(item);
}
- ItemAdded?.Invoke(item, silent);
+ ItemAdded?.Invoke(item);
}
///
@@ -57,7 +54,7 @@ namespace osu.Game.Database
usage.Context.Update(item);
ItemRemoved?.Invoke(item);
- ItemAdded?.Invoke(item, true);
+ ItemAdded?.Invoke(item);
}
///
@@ -71,6 +68,7 @@ namespace osu.Game.Database
Refresh(ref item);
if (item.DeletePending) return false;
+
item.DeletePending = true;
}
@@ -89,10 +87,11 @@ namespace osu.Game.Database
Refresh(ref item, ConsumableItems);
if (!item.DeletePending) return false;
+
item.DeletePending = false;
}
- ItemAdded?.Invoke(item, true);
+ ItemAdded?.Invoke(item);
return true;
}
diff --git a/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs
new file mode 100644
index 0000000000..5d6ff6b09b
--- /dev/null
+++ b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs
@@ -0,0 +1,27 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+using osu.Framework.Platform;
+
+namespace osu.Game.Database
+{
+ public abstract class MutableDatabaseBackedStoreWithFileIncludes : MutableDatabaseBackedStore
+ where T : class, IHasPrimaryKey, ISoftDelete, IHasFiles
+ where U : INamedFileInfo
+ {
+ protected MutableDatabaseBackedStoreWithFileIncludes(IDatabaseContextFactory contextFactory, Storage storage = null)
+ : base(contextFactory, storage)
+ {
+ }
+
+ protected override IQueryable AddIncludesForConsumption(IQueryable query) =>
+ base.AddIncludesForConsumption(query)
+ .Include(s => s.Files).ThenInclude(f => f.FileInfo);
+
+ protected override IQueryable AddIncludesForDeletion(IQueryable query) =>
+ base.AddIncludesForDeletion(query)
+ .Include(s => s.Files); // don't include FileInfo. these are handled by the FileStore itself.
+ }
+}
diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs
index f4abcd6496..ebd9db786f 100644
--- a/osu.Game/Graphics/Backgrounds/Triangles.cs
+++ b/osu.Game/Graphics/Backgrounds/Triangles.cs
@@ -86,7 +86,7 @@ namespace osu.Game.Graphics.Backgrounds
public float TriangleScale
{
- get { return triangleScale; }
+ get => triangleScale;
set
{
float change = value / triangleScale;
@@ -110,10 +110,10 @@ namespace osu.Game.Graphics.Backgrounds
if (CreateNewTriangles)
addTriangles(false);
- float adjustedAlpha = HideAlphaDiscrepancies ?
+ float adjustedAlpha = HideAlphaDiscrepancies
// Cubically scale alpha to make it drop off more sharply.
- (float)Math.Pow(DrawColourInfo.Colour.AverageColour.Linear.A, 3) :
- 1;
+ ? (float)Math.Pow(DrawColourInfo.Colour.AverageColour.Linear.A, 3)
+ : 1;
float elapsedSeconds = (float)Time.Elapsed / 1000;
// Since position is relative, the velocity needs to scale inversely with DrawHeight.
@@ -181,6 +181,7 @@ namespace osu.Game.Graphics.Backgrounds
protected override DrawNode CreateDrawNode() => new TrianglesDrawNode();
private readonly TrianglesDrawNodeSharedData sharedData = new TrianglesDrawNodeSharedData();
+
protected override void ApplyDrawNode(DrawNode node)
{
base.ApplyDrawNode(node);
diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs
index 31eea2cd0c..621eeea2b7 100644
--- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs
+++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs
@@ -3,7 +3,7 @@
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
@@ -74,7 +74,7 @@ namespace osu.Game.Graphics.Containers
}
[BackgroundDependencyLoader]
- private void load(IBindableBeatmap beatmap)
+ private void load(IBindable beatmap)
{
Beatmap.BindTo(beatmap);
}
diff --git a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs
index e2e1385f3e..c1811f37d5 100644
--- a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs
+++ b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs
@@ -15,15 +15,9 @@ namespace osu.Game.Graphics.Containers
{
public Drawable Icon
{
- get
- {
- return InternalChild;
- }
+ get => InternalChild;
- set
- {
- InternalChild = value;
- }
+ set => InternalChild = value;
}
///
@@ -33,8 +27,8 @@ namespace osu.Game.Graphics.Containers
///
public new EdgeEffectParameters EdgeEffect
{
- get { return base.EdgeEffect; }
- set { base.EdgeEffect = value; }
+ get => base.EdgeEffect;
+ set => base.EdgeEffect = value;
}
protected override void Update()
diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs
index d23568e6be..a5b5b7af42 100644
--- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs
+++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index 3c4d94e970..c6ee91f961 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -4,9 +4,9 @@
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osuTK;
-using osu.Framework.Configuration;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Audio;
@@ -24,7 +24,17 @@ namespace osu.Game.Graphics.Containers
protected override bool BlockNonPositionalInput => true;
- private PreviewTrackManager previewTrackManager;
+ ///
+ /// Temporary to allow for overlays in the main screen content to not dim theirselves.
+ /// Should be eventually replaced by dimming which is aware of the target dim container (traverse parent for certain interface type?).
+ ///
+ protected virtual bool DimMainContent => true;
+
+ [Resolved(CanBeNull = true)]
+ private OsuGame osuGame { get; set; }
+
+ [Resolved]
+ private PreviewTrackManager previewTrackManager { get; set; }
protected readonly Bindable OverlayActivationMode = new Bindable(OverlayActivation.All);
@@ -36,10 +46,8 @@ namespace osu.Game.Graphics.Containers
}
[BackgroundDependencyLoader(true)]
- private void load(OsuGame osuGame, AudioManager audio, PreviewTrackManager previewTrackManager)
+ private void load(AudioManager audio)
{
- this.previewTrackManager = previewTrackManager;
-
if (osuGame != null)
OverlayActivationMode.BindTo(osuGame.OverlayActivationMode);
@@ -90,15 +98,18 @@ namespace osu.Game.Graphics.Containers
switch (visibility)
{
case Visibility.Visible:
- if (OverlayActivationMode != OverlayActivation.Disabled)
+ if (OverlayActivationMode.Value != OverlayActivation.Disabled)
{
if (PlaySamplesOnStateChange) samplePopIn?.Play();
+ if (BlockScreenWideMouse && DimMainContent) osuGame?.AddBlockingOverlay(this);
}
else
State = Visibility.Hidden;
+
break;
case Visibility.Hidden:
if (PlaySamplesOnStateChange) samplePopOut?.Play();
+ if (BlockScreenWideMouse) osuGame?.RemoveBlockingOverlay(this);
break;
}
}
@@ -108,5 +119,11 @@ namespace osu.Game.Graphics.Containers
base.PopOut();
previewTrackManager.StopAnyPlaying(this);
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ osuGame?.RemoveBlockingOverlay(this);
+ }
}
}
diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs
index f94fb2540c..1e0b56dae3 100644
--- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Graphics.Containers
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- if(HoverColour == default)
+ if (HoverColour == default)
HoverColour = colours.Yellow;
}
diff --git a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs
index e8b8537807..56e5f411b8 100644
--- a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs
@@ -12,7 +12,8 @@ namespace osu.Game.Graphics.Containers
{
public class OsuTextFlowContainer : TextFlowContainer
{
- public OsuTextFlowContainer(Action defaultCreationParameters = null) : base(defaultCreationParameters)
+ public OsuTextFlowContainer(Action defaultCreationParameters = null)
+ : base(defaultCreationParameters)
{
}
diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs
index f7d30dc109..f65a0a469a 100644
--- a/osu.Game/Graphics/Containers/ParallaxContainer.cs
+++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs
@@ -6,8 +6,8 @@ using osu.Framework.Graphics;
using osu.Framework.Input;
using osuTK;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Game.Configuration;
-using osu.Framework.Configuration;
using osu.Framework.MathUtils;
namespace osu.Game.Graphics.Containers
@@ -45,7 +45,7 @@ namespace osu.Game.Graphics.Containers
parallaxEnabled = config.GetBindable(OsuSetting.MenuParallax);
parallaxEnabled.ValueChanged += delegate
{
- if (!parallaxEnabled)
+ if (!parallaxEnabled.Value)
{
content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, Easing.OutQuint);
content.Scale = new Vector2(1 + System.Math.Abs(ParallaxAmount));
@@ -65,7 +65,7 @@ namespace osu.Game.Graphics.Containers
{
base.Update();
- if (parallaxEnabled)
+ if (parallaxEnabled.Value)
{
Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.Position) - DrawSize / 2) * ParallaxAmount;
diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs
index a20c17cc8e..51f068d920 100644
--- a/osu.Game/Graphics/Containers/ScalingContainer.cs
+++ b/osu.Game/Graphics/Containers/ScalingContainer.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
@@ -74,10 +74,10 @@ namespace osu.Game.Graphics.Containers
}
}
- private void scaleChanged(float value)
+ private void scaleChanged(ValueChangedEvent args)
{
- this.ScaleTo(new Vector2(value), 500, Easing.Out);
- this.ResizeTo(new Vector2(1 / value), 500, Easing.Out);
+ this.ScaleTo(new Vector2(args.NewValue), 500, Easing.Out);
+ this.ResizeTo(new Vector2(1 / args.NewValue), 500, Easing.Out);
}
}
@@ -108,7 +108,7 @@ namespace osu.Game.Graphics.Containers
sizableContainer.FinishTransforms();
}
- private bool requiresBackgroundVisible => (scalingMode == ScalingMode.Everything || scalingMode == ScalingMode.ExcludeOverlays) && (sizeX.Value != 1 || sizeY.Value != 1);
+ private bool requiresBackgroundVisible => (scalingMode.Value == ScalingMode.Everything || scalingMode.Value == ScalingMode.ExcludeOverlays) && (sizeX.Value != 1 || sizeY.Value != 1);
private void updateSize()
{
@@ -137,8 +137,8 @@ namespace osu.Game.Graphics.Containers
bool scaling = targetMode == null || scalingMode.Value == targetMode;
- var targetSize = scaling ? new Vector2(sizeX, sizeY) : Vector2.One;
- var targetPosition = scaling ? new Vector2(posX, posY) * (Vector2.One - targetSize) : Vector2.Zero;
+ var targetSize = scaling ? new Vector2(sizeX.Value, sizeY.Value) : Vector2.One;
+ var targetPosition = scaling ? new Vector2(posX.Value, posY.Value) * (Vector2.One - targetSize) : Vector2.Zero;
bool requiresMasking = scaling && targetSize != Vector2.One;
if (requiresMasking)
diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs
index 1f212a5fba..4cd248d612 100644
--- a/osu.Game/Graphics/Containers/SectionsContainer.cs
+++ b/osu.Game/Graphics/Containers/SectionsContainer.cs
@@ -3,7 +3,7 @@
using System;
using System.Linq;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -24,7 +24,7 @@ namespace osu.Game.Graphics.Containers
public Drawable ExpandableHeader
{
- get { return expandableHeader; }
+ get => expandableHeader;
set
{
if (value == expandableHeader) return;
@@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Containers
public Drawable FixedHeader
{
- get { return fixedHeader; }
+ get => fixedHeader;
set
{
if (value == fixedHeader) return;
@@ -56,7 +56,7 @@ namespace osu.Game.Graphics.Containers
public Drawable Footer
{
- get { return footer; }
+ get => footer;
set
{
if (value == footer) return;
@@ -75,7 +75,7 @@ namespace osu.Game.Graphics.Containers
public Drawable HeaderBackground
{
- get { return headerBackground; }
+ get => headerBackground;
set
{
if (value == headerBackground) return;
@@ -110,6 +110,7 @@ namespace osu.Game.Graphics.Containers
private float headerHeight, footerHeight;
private readonly MarginPadding originalSectionsMargin;
+
private void updateSectionsMargin()
{
if (!Children.Any()) return;
@@ -153,6 +154,7 @@ namespace osu.Game.Graphics.Containers
}
private float lastKnownScroll;
+
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs
new file mode 100644
index 0000000000..4c8928e122
--- /dev/null
+++ b/osu.Game/Graphics/Containers/UserDimContainer.cs
@@ -0,0 +1,88 @@
+// 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.Game.Configuration;
+using osuTK.Graphics;
+
+namespace osu.Game.Graphics.Containers
+{
+ ///
+ /// A container that applies user-configured dim levels to its contents.
+ /// This container specifies behavior that applies to both Storyboards and Backgrounds.
+ ///
+ public class UserDimContainer : Container
+ {
+ private const float background_fade_duration = 800;
+
+ private Bindable dimLevel { get; set; }
+
+ private Bindable showStoryboard { get; set; }
+
+ ///
+ /// Whether or not user-configured dim levels should be applied to the container.
+ ///
+ public readonly Bindable EnableUserDim = new Bindable();
+
+ ///
+ /// Whether or not the storyboard loaded should completely hide the background behind it.
+ ///
+ public readonly Bindable StoryboardReplacesBackground = new Bindable();
+
+ protected Container DimContainer { get; }
+
+ protected override Container Content => DimContainer;
+
+ private readonly bool isStoryboard;
+
+ ///
+ /// Creates a new .
+ ///
+ /// Whether or not this instance of UserDimContainer contains a storyboard.
+ ///
+ /// While both backgrounds and storyboards allow user dim levels to be applied, storyboards can be toggled via
+ /// and can cause backgrounds to become hidden via .
+ ///
+ ///
+ public UserDimContainer(bool isStoryboard = false)
+ {
+ this.isStoryboard = isStoryboard;
+ AddInternal(DimContainer = new Container { RelativeSizeAxes = Axes.Both });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config)
+ {
+ dimLevel = config.GetBindable(OsuSetting.DimLevel);
+ showStoryboard = config.GetBindable(OsuSetting.ShowStoryboard);
+ EnableUserDim.ValueChanged += _ => updateBackgroundDim();
+ dimLevel.ValueChanged += _ => updateBackgroundDim();
+ showStoryboard.ValueChanged += _ => updateBackgroundDim();
+ StoryboardReplacesBackground.ValueChanged += _ => updateBackgroundDim();
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ updateBackgroundDim();
+ }
+
+ private void updateBackgroundDim()
+ {
+ if (isStoryboard)
+ {
+ DimContainer.FadeTo(!showStoryboard.Value || dimLevel.Value == 1 ? 0 : 1, background_fade_duration, Easing.OutQuint);
+ }
+ else
+ {
+ // The background needs to be hidden in the case of it being replaced by the storyboard
+ DimContainer.FadeTo(showStoryboard.Value && StoryboardReplacesBackground.Value ? 0 : 1, background_fade_duration, Easing.OutQuint);
+ }
+
+ DimContainer.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)dimLevel.Value) : Color4.White, background_fade_duration, Easing.OutQuint);
+ }
+ }
+}
diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs
index 76c2345cd5..059beeca4d 100644
--- a/osu.Game/Graphics/Cursor/MenuCursor.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursor.cs
@@ -3,7 +3,6 @@
using osuTK;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
@@ -11,6 +10,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using System;
using JetBrains.Annotations;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events;
using osuTK.Input;
@@ -80,11 +80,12 @@ namespace osu.Game.Graphics.Cursor
activeCursor.AdditiveLayer.FadeInFromZero(800, Easing.OutQuint);
}
- if (e.Button == MouseButton.Left && cursorRotate)
+ if (e.Button == MouseButton.Left && cursorRotate.Value)
{
dragRotationState = DragRotationState.DragStarted;
positionMouseDown = e.MousePosition;
}
+
return base.OnMouseDown(e);
}
@@ -102,6 +103,7 @@ namespace osu.Game.Graphics.Cursor
activeCursor.RotateTo(0, 600 * (1 + Math.Abs(activeCursor.Rotation / 720)), Easing.OutElasticHalf);
dragRotationState = DragRotationState.NotDragging;
}
+
return base.OnMouseUp(e);
}
@@ -156,7 +158,7 @@ namespace osu.Game.Graphics.Cursor
};
cursorScale = config.GetBindable(OsuSetting.MenuCursorSize);
- cursorScale.ValueChanged += newScale => cursorContainer.Scale = new Vector2((float)newScale * base_scale);
+ cursorScale.ValueChanged += scale => cursorContainer.Scale = new Vector2((float)scale.NewValue * base_scale);
cursorScale.TriggerChange();
}
}
diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs
index a8870f3cf3..92e5ba6195 100644
--- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs
@@ -43,6 +43,7 @@ namespace osu.Game.Graphics.Cursor
}
private IProvideCursor currentTarget;
+
protected override void Update()
{
base.Update();
@@ -50,6 +51,7 @@ namespace osu.Game.Graphics.Cursor
if (!CanShowCursor)
{
currentTarget?.Cursor?.Hide();
+ currentTarget = null;
return;
}
diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs
index 8f451054ac..4e0ce4a3e1 100644
--- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs
+++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs
@@ -17,7 +17,8 @@ namespace osu.Game.Graphics.Cursor
{
protected override ITooltip CreateTooltip() => new OsuTooltip();
- public OsuTooltipContainer(CursorContainer cursor) : base(cursor)
+ public OsuTooltipContainer(CursorContainer cursor)
+ : base(cursor)
{
}
@@ -46,8 +47,6 @@ namespace osu.Game.Graphics.Cursor
}
}
- private const float text_size = 16;
-
public OsuTooltip()
{
AutoSizeEasing = Easing.OutQuint;
@@ -69,9 +68,8 @@ namespace osu.Game.Graphics.Cursor
},
text = new OsuSpriteText
{
- TextSize = text_size,
Padding = new MarginPadding(5),
- Font = @"Exo2.0-Regular",
+ Font = OsuFont.GetFont(weight: FontWeight.Regular)
}
};
}
diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs
index be1c9e0cfc..3ae1033f5d 100644
--- a/osu.Game/Graphics/DrawableDate.cs
+++ b/osu.Game/Graphics/DrawableDate.cs
@@ -21,6 +21,7 @@ namespace osu.Game.Graphics
{
if (date == value)
return;
+
date = value.ToLocalTime();
if (LoadState >= LoadState.Ready)
@@ -30,8 +31,7 @@ namespace osu.Game.Graphics
public DrawableDate(DateTimeOffset date)
{
- Font = "Exo2.0-RegularItalic";
-
+ Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true);
Date = date;
}
diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs
new file mode 100644
index 0000000000..dc660fd159
--- /dev/null
+++ b/osu.Game/Graphics/OsuFont.cs
@@ -0,0 +1,117 @@
+// 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;
+
+namespace osu.Game.Graphics
+{
+ public struct OsuFont
+ {
+ ///
+ /// The default font size.
+ ///
+ public const float DEFAULT_FONT_SIZE = 16;
+
+ ///
+ /// The default font.
+ ///
+ public static FontUsage Default => GetFont();
+
+ public static FontUsage Numeric => GetFont(Typeface.Venera, weight: FontWeight.Regular);
+
+ ///
+ /// 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(Typeface typeface = Typeface.Exo, 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(Typeface typeface)
+ {
+ switch (typeface)
+ {
+ case Typeface.Exo:
+ return "Exo2.0";
+ case Typeface.FontAwesome:
+ return "FontAwesome";
+ case Typeface.Venera:
+ return "Venera";
+ }
+
+ return null;
+ }
+
+ ///
+ /// Retrieves the string representation of a .
+ ///
+ /// The .
+ /// The .
+ /// The string representation of in the specified .
+ public static string GetWeightString(Typeface typeface, FontWeight weight)
+ => GetWeightString(GetFamilyString(typeface), weight);
+
+ ///
+ /// Retrieves the string representation of a .
+ ///
+ /// The .
+ /// 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 (family != GetFamilyString(Typeface.Exo) && weight == FontWeight.Regular)
+ weightString = string.Empty;
+
+ return weightString;
+ }
+ }
+
+ public static class OsuFontExtensions
+ {
+ ///
+ /// Creates a new by applying adjustments to this .
+ ///
+ /// The font typeface. If null, the value is copied from this .
+ /// The text size. If null, the value is copied from this .
+ /// The font weight. If null, the value is copied from this .
+ /// Whether the font is italic. If null, the value is copied from this .
+ /// Whether all characters should be spaced apart the same distance. If null, the value is copied from this .
+ /// The resulting .
+ public static FontUsage With(this FontUsage usage, Typeface? typeface = null, float? size = null, FontWeight? weight = null, bool? italics = null, bool? fixedWidth = null)
+ {
+ string familyString = typeface != null ? OsuFont.GetFamilyString(typeface.Value) : usage.Family;
+ string weightString = weight != null ? OsuFont.GetWeightString(familyString, weight.Value) : usage.Weight;
+
+ return usage.With(familyString, size, weightString, italics, fixedWidth);
+ }
+ }
+
+ public enum Typeface
+ {
+ Exo,
+ FontAwesome,
+ Venera,
+ }
+
+ public enum FontWeight
+ {
+ Light,
+ Regular,
+ Medium,
+ SemiBold,
+ Bold,
+ Black
+ }
+}
diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs
index ef4209f6f3..a2ac71de93 100644
--- a/osu.Game/Graphics/ScreenshotManager.cs
+++ b/osu.Game/Graphics/ScreenshotManager.cs
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
@@ -127,7 +127,7 @@ namespace osu.Game.Graphics
{
base.Update();
- if (cursorVisibility == false && Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0)
+ if (cursorVisibility.Value == false && Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0)
cursorVisibility.Value = true;
}
diff --git a/osu.Game/Graphics/SpriteIcon.cs b/osu.Game/Graphics/SpriteIcon.cs
index dcb29d50ea..f7d7d21435 100644
--- a/osu.Game/Graphics/SpriteIcon.cs
+++ b/osu.Game/Graphics/SpriteIcon.cs
@@ -65,6 +65,7 @@ namespace osu.Game.Graphics
}
private FontAwesome loadedIcon;
+
private void updateTexture()
{
var loadableIcon = icon;
@@ -104,9 +105,10 @@ namespace osu.Game.Graphics
}
private bool shadow;
+
public bool Shadow
{
- get { return shadow; }
+ get => shadow;
set
{
shadow = value;
@@ -119,11 +121,7 @@ namespace osu.Game.Graphics
public FontAwesome Icon
{
- get
- {
- return icon;
- }
-
+ get => icon;
set
{
if (icon == value) return;
diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs
index a0c025f4fa..ed771bb03f 100644
--- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs
+++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs
@@ -9,12 +9,10 @@ namespace osu.Game.Graphics.Sprites
{
public class OsuSpriteText : SpriteText
{
- public const float FONT_SIZE = 16;
-
public OsuSpriteText()
{
Shadow = true;
- TextSize = FONT_SIZE;
+ Font = OsuFont.Default;
}
}
diff --git a/osu.Game/Graphics/UserInterface/Bar.cs b/osu.Game/Graphics/UserInterface/Bar.cs
index 92b71a1cc3..2a858ccbcf 100644
--- a/osu.Game/Graphics/UserInterface/Bar.cs
+++ b/osu.Game/Graphics/UserInterface/Bar.cs
@@ -26,10 +26,7 @@ namespace osu.Game.Graphics.UserInterface
///
public float Length
{
- get
- {
- return length;
- }
+ get => length;
set
{
length = MathHelper.Clamp(value, 0, 1);
@@ -39,35 +36,21 @@ namespace osu.Game.Graphics.UserInterface
public Color4 BackgroundColour
{
- get
- {
- return background.Colour;
- }
- set
- {
- background.Colour = value;
- }
+ get => background.Colour;
+ set => background.Colour = value;
}
public Color4 AccentColour
{
- get
- {
- return bar.Colour;
- }
- set
- {
- bar.Colour = value;
- }
+ get => bar.Colour;
+ set => bar.Colour = value;
}
private BarDirection direction = BarDirection.LeftToRight;
+
public BarDirection Direction
{
- get
- {
- return direction;
- }
+ get => direction;
set
{
direction = value;
diff --git a/osu.Game/Graphics/UserInterface/BarGraph.cs b/osu.Game/Graphics/UserInterface/BarGraph.cs
index 97335e3c42..58058c9d4c 100644
--- a/osu.Game/Graphics/UserInterface/BarGraph.cs
+++ b/osu.Game/Graphics/UserInterface/BarGraph.cs
@@ -17,12 +17,10 @@ namespace osu.Game.Graphics.UserInterface
public float? MaxValue { get; set; }
private BarDirection direction = BarDirection.BottomToTop;
+
public new BarDirection Direction
{
- get
- {
- return direction;
- }
+ get => direction;
set
{
direction = value;
@@ -69,6 +67,7 @@ namespace osu.Game.Graphics.UserInterface
});
}
}
+
//I'm using ToList() here because Where() returns an Enumerable which can change it's elements afterwards
RemoveRange(Children.Where((bar, index) => index >= value.Count()).ToList());
}
diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
index b9196adda3..40bc98a654 100644
--- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
+++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
@@ -27,12 +27,12 @@ namespace osu.Game.Graphics.UserInterface
{
Height = 32;
TabContainer.Spacing = new Vector2(padding, 0f);
- Current.ValueChanged += tab =>
+ Current.ValueChanged += index =>
{
foreach (var t in TabContainer.Children.OfType())
{
var tIndex = TabContainer.IndexOf(t);
- var tabIndex = TabContainer.IndexOf(TabMap[tab]);
+ var tabIndex = TabContainer.IndexOf(TabMap[index.NewValue]);
t.State = tIndex < tabIndex ? Visibility.Hidden : Visibility.Visible;
t.Chevron.FadeTo(tIndex <= tabIndex ? 0f : 1f, 500, Easing.OutQuint);
@@ -57,10 +57,11 @@ namespace osu.Game.Graphics.UserInterface
public Visibility State
{
- get { return state; }
+ get => state;
set
{
if (value == state) return;
+
state = value;
const float transition_duration = 500;
@@ -80,9 +81,10 @@ namespace osu.Game.Graphics.UserInterface
}
}
- public BreadcrumbTabItem(T value) : base(value)
+ public BreadcrumbTabItem(T value)
+ : base(value)
{
- Text.TextSize = 18;
+ Text.Font = Text.Font.With(size: 18);
Text.Margin = new MarginPadding { Vertical = 8 };
Padding = new MarginPadding { Right = padding + item_chevron_size };
Add(Chevron = new SpriteIcon
diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs
index 644f66a92d..dbbe5b4258 100644
--- a/osu.Game/Graphics/UserInterface/DialogButton.cs
+++ b/osu.Game/Graphics/UserInterface/DialogButton.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.Framework.Bindables;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Graphics;
@@ -12,7 +13,6 @@ using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Containers;
-using osu.Framework.Configuration;
using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
@@ -141,8 +141,7 @@ namespace osu.Game.Graphics.UserInterface
Text = Text,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- TextSize = 28,
- Font = "Exo2.0-Bold",
+ Font = OsuFont.GetFont(size: 28, weight: FontWeight.Bold),
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.1f),
Colour = Color4.White,
@@ -155,12 +154,10 @@ namespace osu.Game.Graphics.UserInterface
}
private Color4 buttonColour;
+
public Color4 ButtonColour
{
- get
- {
- return buttonColour;
- }
+ get => buttonColour;
set
{
buttonColour = value;
@@ -170,12 +167,10 @@ namespace osu.Game.Graphics.UserInterface
}
private Color4 backgroundColour = OsuColour.Gray(34);
+
public Color4 BackgroundColour
{
- get
- {
- return backgroundColour;
- }
+ get => backgroundColour;
set
{
backgroundColour = value;
@@ -184,12 +179,10 @@ namespace osu.Game.Graphics.UserInterface
}
private string text;
+
public string Text
{
- get
- {
- return text;
- }
+ get => text;
set
{
text = value;
@@ -197,18 +190,10 @@ namespace osu.Game.Graphics.UserInterface
}
}
- private float textSize = 28;
public float TextSize
{
- get
- {
- return textSize;
- }
- set
- {
- textSize = value;
- spriteText.TextSize = value;
- }
+ get => spriteText.Font.Size;
+ set => spriteText.Font = spriteText.Font.With(size: value);
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos);
@@ -242,9 +227,9 @@ namespace osu.Game.Graphics.UserInterface
Selected.Value = false;
}
- private void selectionChanged(bool isSelected)
+ private void selectionChanged(ValueChangedEvent args)
{
- if (isSelected)
+ if (args.NewValue)
{
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic);
diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
index 309021b7d6..2ed37799f6 100644
--- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
+++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnClick(ClickEvent e)
{
- if(Link != null)
+ if (Link != null)
host.OpenUrlExternally(Link);
return true;
}
diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
index 4f84108d8a..cbbaa6d303 100644
--- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
+++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
@@ -17,7 +17,8 @@ namespace osu.Game.Graphics.UserInterface
{
private SampleChannel sampleClick;
- public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) : base(sampleSet)
+ public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal)
+ : base(sampleSet)
{
}
diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs
index b2cd4aab19..b246092a7f 100644
--- a/osu.Game/Graphics/UserInterface/HoverSounds.cs
+++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs
@@ -45,8 +45,10 @@ namespace osu.Game.Graphics.UserInterface
{
[Description("")]
Loud,
+
[Description("-soft")]
Normal,
+
[Description("-softer")]
Soft
}
diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs
index b9062b8d39..025aa30986 100644
--- a/osu.Game/Graphics/UserInterface/IconButton.cs
+++ b/osu.Game/Graphics/UserInterface/IconButton.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Graphics.UserInterface
///
public Color4 IconColour
{
- get { return iconColour ?? Color4.White; }
+ get => iconColour ?? Color4.White;
set
{
iconColour = value;
@@ -34,8 +34,8 @@ namespace osu.Game.Graphics.UserInterface
///
public Color4 IconHoverColour
{
- get { return iconHoverColour ?? IconColour; }
- set { iconHoverColour = value; }
+ get => iconHoverColour ?? IconColour;
+ set => iconHoverColour = value;
}
///
@@ -43,8 +43,8 @@ namespace osu.Game.Graphics.UserInterface
///
public FontAwesome Icon
{
- get { return icon.Icon; }
- set { icon.Icon = value; }
+ get => icon.Icon;
+ set => icon.Icon = value;
}
///
@@ -52,8 +52,8 @@ namespace osu.Game.Graphics.UserInterface
///
public Vector2 IconScale
{
- get { return icon.Scale; }
- set { icon.Scale = value; }
+ get => icon.Scale;
+ set => icon.Scale = value;
}
///
diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs
index e281840cc3..f16e5ea002 100644
--- a/osu.Game/Graphics/UserInterface/LineGraph.cs
+++ b/osu.Game/Graphics/UserInterface/LineGraph.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Graphics.UserInterface
///
public IEnumerable Values
{
- get { return values; }
+ get => values;
set
{
values = value.ToArray();
@@ -118,6 +118,7 @@ namespace osu.Game.Graphics.UserInterface
protected float GetYPosition(float value)
{
if (ActualMaxValue == ActualMinValue) return 0;
+
return (ActualMaxValue - value) / (ActualMaxValue - ActualMinValue);
}
}
diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs
index 0405a09a78..1f5195eaf1 100644
--- a/osu.Game/Graphics/UserInterface/Nub.cs
+++ b/osu.Game/Graphics/UserInterface/Nub.cs
@@ -5,7 +5,7 @@ using System;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -41,9 +41,9 @@ namespace osu.Game.Graphics.UserInterface
},
};
- Current.ValueChanged += newValue =>
+ Current.ValueChanged += filled =>
{
- if (newValue)
+ if (filled.NewValue)
fill.FadeIn(200, Easing.OutQuint);
else
fill.FadeTo(0.01f, 200, Easing.OutQuint); //todo: remove once we figure why containers aren't drawing at all times
@@ -72,9 +72,10 @@ namespace osu.Game.Graphics.UserInterface
}
private bool glowing;
+
public bool Glowing
{
- get { return glowing; }
+ get => glowing;
set
{
glowing = value;
@@ -94,10 +95,7 @@ namespace osu.Game.Graphics.UserInterface
public bool Expanded
{
- set
- {
- this.ResizeTo(new Vector2(value ? EXPANDED_SIZE : COLLAPSED_SIZE, 12), 500, Easing.OutQuint);
- }
+ set => this.ResizeTo(new Vector2(value ? EXPANDED_SIZE : COLLAPSED_SIZE, 12), 500, Easing.OutQuint);
}
private readonly Bindable current = new Bindable();
@@ -116,9 +114,10 @@ namespace osu.Game.Graphics.UserInterface
}
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
accentColour = value;
@@ -128,9 +127,10 @@ namespace osu.Game.Graphics.UserInterface
}
private Color4 glowingAccentColour;
+
public Color4 GlowingAccentColour
{
- get { return glowingAccentColour; }
+ get => glowingAccentColour;
set
{
glowingAccentColour = value;
@@ -140,9 +140,10 @@ namespace osu.Game.Graphics.UserInterface
}
private Color4 glowColour;
+
public Color4 GlowColour
{
- get { return glowColour; }
+ get => glowColour;
set
{
glowColour = value;
diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
index 9d9f95662b..d64068f74c 100644
--- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
@@ -73,7 +73,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- Enabled.BindValueChanged(enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint), true);
+ Enabled.BindValueChanged(enabled => this.FadeColour(enabled.NewValue ? Color4.White : colours.Gray9, 200, Easing.OutQuint), true);
}
protected override bool OnHover(HoverEvent e)
diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs
index 6ba461ad70..7a27f825f6 100644
--- a/osu.Game/Graphics/UserInterface/OsuButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuButton.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
@@ -46,13 +47,13 @@ namespace osu.Game.Graphics.UserInterface
new HoverClickSounds(HoverSampleSet.Loud),
});
- Enabled.ValueChanged += enabled_ValueChanged;
+ Enabled.ValueChanged += enabledChanged;
Enabled.TriggerChange();
}
- private void enabled_ValueChanged(bool enabled)
+ private void enabledChanged(ValueChangedEvent e)
{
- this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
+ this.FadeColour(e.NewValue ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
}
protected override bool OnHover(HoverEvent e)
@@ -84,7 +85,7 @@ namespace osu.Game.Graphics.UserInterface
Depth = -1,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(weight: FontWeight.Bold)
};
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
index b71bdcc593..de3d93d845 100644
--- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
@@ -4,7 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
@@ -33,7 +33,7 @@ namespace osu.Game.Graphics.UserInterface
public string LabelText
{
- get { return labelSpriteText?.Text; }
+ get => labelSpriteText?.Text;
set
{
if (labelSpriteText != null)
@@ -43,7 +43,7 @@ namespace osu.Game.Graphics.UserInterface
public MarginPadding LabelPadding
{
- get { return labelSpriteText?.Padding ?? new MarginPadding(); }
+ get => labelSpriteText?.Padding ?? new MarginPadding();
set
{
if (labelSpriteText != null)
@@ -86,9 +86,9 @@ namespace osu.Game.Graphics.UserInterface
{
base.LoadComplete();
- Current.ValueChanged += newValue =>
+ Current.ValueChanged += enabled =>
{
- if (newValue)
+ if (enabled.NewValue)
sampleChecked?.Play();
else
sampleUnchecked?.Play();
diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
index 0877776d0e..db38067a50 100644
--- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
@@ -17,9 +17,10 @@ namespace osu.Game.Graphics.UserInterface
public class OsuDropdown : Dropdown, IHasAccentColour
{
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
accentColour = value;
@@ -37,11 +38,9 @@ namespace osu.Game.Graphics.UserInterface
private void updateAccentColour()
{
- var header = Header as IHasAccentColour;
- if (header != null) header.AccentColour = accentColour;
+ if (Header is IHasAccentColour header) header.AccentColour = accentColour;
- var menu = Menu as IHasAccentColour;
- if (menu != null) menu.AccentColour = accentColour;
+ if (Menu is IHasAccentColour menu) menu.AccentColour = accentColour;
}
protected override DropdownHeader CreateHeader() => new OsuDropdownHeader();
@@ -49,6 +48,7 @@ namespace osu.Game.Graphics.UserInterface
protected override DropdownMenu CreateMenu() => new OsuDropdownMenu();
#region OsuDropdownMenu
+
protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour
{
public override bool HandleNonPositionalInput => State == MenuState.Open;
@@ -83,9 +83,10 @@ namespace osu.Game.Graphics.UserInterface
}
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
accentColour = value;
@@ -97,15 +98,17 @@ namespace osu.Game.Graphics.UserInterface
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour };
#region DrawableOsuDropdownMenuItem
+
public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
{
// IsHovered is used
public override bool HandlePositionalInput => true;
private Color4? accentColour;
+
public Color4 AccentColour
{
- get { return accentColour ?? nonAccentSelectedColour; }
+ get => accentColour ?? nonAccentSelectedColour;
set
{
accentColour = value;
@@ -149,8 +152,7 @@ namespace osu.Game.Graphics.UserInterface
{
base.UpdateForegroundColour();
- var content = Foreground.Children.FirstOrDefault() as Content;
- if (content != null) content.Chevron.Alpha = IsHovered ? 1 : 0;
+ if (Foreground.Children.FirstOrDefault() is Content content) content.Chevron.Alpha = IsHovered ? 1 : 0;
}
protected override Drawable CreateContent() => new Content();
@@ -159,8 +161,8 @@ namespace osu.Game.Graphics.UserInterface
{
public string Text
{
- get { return Label.Text; }
- set { Label.Text = value; }
+ get => Label.Text;
+ set => Label.Text = value;
}
public readonly OsuSpriteText Label;
@@ -194,25 +196,29 @@ namespace osu.Game.Graphics.UserInterface
}
}
}
+
#endregion
}
+
#endregion
public class OsuDropdownHeader : DropdownHeader, IHasAccentColour
{
protected readonly SpriteText Text;
+
protected override string Label
{
- get { return Text.Text; }
- set { Text.Text = value; }
+ get => Text.Text;
+ set => Text.Text = value;
}
protected readonly SpriteIcon Icon;
private Color4 accentColour;
+
public virtual Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
accentColour = value;
diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs
index 0a1dfe8c41..9b5755ea8b 100644
--- a/osu.Game/Graphics/UserInterface/OsuMenu.cs
+++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs
@@ -125,7 +125,7 @@ namespace osu.Game.Graphics.UserInterface
{
public string Text
{
- get { return NormalText.Text; }
+ get => NormalText.Text;
set
{
NormalText.Text = value;
@@ -149,7 +149,7 @@ namespace osu.Game.Graphics.UserInterface
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
- TextSize = text_size,
+ Font = OsuFont.GetFont(size: text_size),
Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL },
},
BoldText = new OsuSpriteText
@@ -158,8 +158,7 @@ namespace osu.Game.Graphics.UserInterface
Alpha = 0,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
- TextSize = text_size,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold),
Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL },
}
};
diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
index 81690f3728..aeb974681d 100644
--- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
@@ -9,12 +9,13 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Platform;
namespace osu.Game.Graphics.UserInterface
{
- public class OsuPasswordTextBox : OsuTextBox
+ public class OsuPasswordTextBox : OsuTextBox, ISuppressKeyEventLogging
{
protected override Drawable GetDrawableCharacter(char c) => new PasswordMaskChar(CalculatedTextSize);
diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
index c66a20a115..c558fd7c7b 100644
--- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
+++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
@@ -35,9 +35,10 @@ namespace osu.Game.Graphics.UserInterface
public virtual string TooltipText { get; private set; }
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
accentColour = value;
@@ -79,10 +80,7 @@ namespace osu.Game.Graphics.UserInterface
new HoverClickSounds()
};
- Current.DisabledChanged += disabled =>
- {
- Alpha = disabled ? 0.3f : 1;
- };
+ Current.DisabledChanged += disabled => { Alpha = disabled ? 0.3f : 1; };
}
[BackgroundDependencyLoader]
@@ -95,7 +93,7 @@ namespace osu.Game.Graphics.UserInterface
protected override void LoadComplete()
{
base.LoadComplete();
- CurrentNumber.BindValueChanged(updateTooltipText, true);
+ CurrentNumber.BindValueChanged(current => updateTooltipText(current.NewValue), true);
}
protected override bool OnHover(HoverEvent e)
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
index 7089b7a79a..e2a4955011 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
@@ -6,7 +6,7 @@ using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -58,14 +58,14 @@ namespace osu.Game.Graphics.UserInterface
}
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
accentColour = value;
- var dropdown = Dropdown as IHasAccentColour;
- if (dropdown != null)
+ if (Dropdown is IHasAccentColour dropdown)
dropdown.AccentColour = value;
foreach (var i in TabContainer.Children.OfType())
i.AccentColour = value;
@@ -101,13 +101,14 @@ namespace osu.Game.Graphics.UserInterface
protected readonly Box Bar;
private Color4 accentColour;
+
public Color4 AccentColour
{
- get { return accentColour; }
+ get => accentColour;
set
{
accentColour = value;
- if (!Active)
+ if (!Active.Value)
Text.Colour = value;
}
}
@@ -128,14 +129,14 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnHover(HoverEvent e)
{
- if (!Active)
+ if (!Active.Value)
fadeActive();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
- if (!Active)
+ if (!Active.Value)
fadeInactive();
}
@@ -146,7 +147,8 @@ namespace osu.Game.Graphics.UserInterface
AccentColour = colours.Blue;
}
- public OsuTabItem(T value) : base(value)
+ public OsuTabItem(T value)
+ : base(value)
{
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
@@ -159,7 +161,7 @@ namespace osu.Game.Graphics.UserInterface
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Text = (value as IHasDescription)?.Description ?? (value as Enum)?.GetDescription() ?? value.ToString(),
- TextSize = 14,
+ Font = OsuFont.GetFont(size: 14)
},
Bar = new Box
{
@@ -173,7 +175,7 @@ namespace osu.Game.Graphics.UserInterface
new HoverClickSounds()
};
- Active.BindValueChanged(val => Text.Font = val ? @"Exo2.0-Bold" : @"Exo2.0", true);
+ Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
}
protected override void OnActivated() => fadeActive();
@@ -224,11 +226,7 @@ namespace osu.Game.Graphics.UserInterface
{
public override Color4 AccentColour
{
- get
- {
- return base.AccentColour;
- }
-
+ get => base.AccentColour;
set
{
base.AccentColour = value;
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
index 0626534307..918473ac53 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
@@ -24,14 +24,15 @@ namespace osu.Game.Graphics.UserInterface
private readonly SpriteIcon icon;
private Color4? accentColour;
+
public Color4 AccentColour
{
- get { return accentColour.GetValueOrDefault(); }
+ get => accentColour.GetValueOrDefault();
set
{
accentColour = value;
- if (Current)
+ if (Current.Value)
{
text.Colour = AccentColour;
icon.Colour = AccentColour;
@@ -41,8 +42,8 @@ namespace osu.Game.Graphics.UserInterface
public string Text
{
- get { return text.Text; }
- set { text.Text = value; }
+ get => text.Text;
+ set => text.Text = value;
}
private const float transition_length = 500;
@@ -67,7 +68,7 @@ namespace osu.Game.Graphics.UserInterface
protected override void OnHoverLost(HoverLostEvent e)
{
- if (!Current)
+ if (!Current.Value)
fadeOut();
base.OnHoverLost(e);
@@ -94,11 +95,7 @@ namespace osu.Game.Graphics.UserInterface
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
- text = new OsuSpriteText
- {
- TextSize = 14,
- Font = @"Exo2.0-Bold",
- },
+ text = new OsuSpriteText { Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold) },
icon = new SpriteIcon
{
Size = new Vector2(14),
@@ -118,9 +115,9 @@ namespace osu.Game.Graphics.UserInterface
}
};
- Current.ValueChanged += v =>
+ Current.ValueChanged += selected =>
{
- if (v)
+ if (selected.NewValue)
{
fadeIn();
icon.Icon = FontAwesome.fa_check_circle_o;
diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs
index a481edb06b..21cdfbf5af 100644
--- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Graphics.UserInterface
protected override SpriteText CreatePlaceholder() => new OsuSpriteText
{
- Font = @"Exo2.0-MediumItalic",
+ Font = OsuFont.GetFont(italics: true),
Colour = new Color4(180, 180, 180, 255),
Margin = new MarginPadding { Left = 2 },
};
@@ -57,7 +57,7 @@ namespace osu.Game.Graphics.UserInterface
base.OnFocusLost(e);
}
- protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), TextSize = CalculatedTextSize };
+ protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: CalculatedTextSize) };
public virtual bool OnPressed(GlobalAction action)
{
diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs
index 904951da0e..156a556b5e 100644
--- a/osu.Game/Graphics/UserInterface/PageTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs
@@ -32,7 +32,8 @@ namespace osu.Game.Graphics.UserInterface
protected readonly SpriteText Text;
- public PageTabItem(T value) : base(value)
+ public PageTabItem(T value)
+ : base(value)
{
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
@@ -45,7 +46,7 @@ namespace osu.Game.Graphics.UserInterface
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Text = (value as Enum)?.GetDescription() ?? value.ToString(),
- TextSize = 14,
+ Font = OsuFont.GetFont(size: 14)
},
box = new Box
{
@@ -59,7 +60,7 @@ namespace osu.Game.Graphics.UserInterface
new HoverClickSounds()
};
- Active.BindValueChanged(val => Text.Font = val ? @"Exo2.0-Bold" : @"Exo2.0", true);
+ Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
}
[BackgroundDependencyLoader]
@@ -70,14 +71,14 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnHover(HoverEvent e)
{
- if (!Active)
+ if (!Active.Value)
slideActive();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
- if (!Active)
+ if (!Active.Value)
slideInactive();
}
diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs
index 9205525af3..8254bdda7c 100644
--- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs
+++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Graphics.UserInterface
public PercentageCounter()
{
- DisplayedCountSpriteText.FixedWidth = true;
+ DisplayedCountSpriteText.Font = DisplayedCountSpriteText.Font.With(fixedWidth: true);
Current.Value = DisplayedCount = 1.0f;
}
@@ -41,7 +41,7 @@ namespace osu.Game.Graphics.UserInterface
public override void Increment(double amount)
{
- Current.Value = Current + amount;
+ Current.Value = Current.Value + amount;
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/ProgressBar.cs b/osu.Game/Graphics/UserInterface/ProgressBar.cs
index 04f85e62b7..d271cd121c 100644
--- a/osu.Game/Graphics/UserInterface/ProgressBar.cs
+++ b/osu.Game/Graphics/UserInterface/ProgressBar.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface
public Color4 FillColour
{
- set { fill.FadeColour(value, 150, Easing.OutQuint); }
+ set => fill.FadeColour(value, 150, Easing.OutQuint);
}
public Color4 BackgroundColour
@@ -32,12 +32,12 @@ namespace osu.Game.Graphics.UserInterface
public double EndTime
{
- set { CurrentNumber.MaxValue = value; }
+ set => CurrentNumber.MaxValue = value;
}
public double CurrentTime
{
- set { CurrentNumber.Value = value; }
+ set => CurrentNumber.Value = value;
}
public ProgressBar()
diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs
index 6e261a8fa7..cd244ed7e6 100644
--- a/osu.Game/Graphics/UserInterface/RollingCounter.cs
+++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs
@@ -1,13 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites;
using System;
using System.Collections.Generic;
+using osu.Framework.Bindables;
using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
@@ -45,15 +45,13 @@ namespace osu.Game.Graphics.UserInterface
///
public virtual T DisplayedCount
{
- get
- {
- return displayedCount;
- }
+ get => displayedCount;
set
{
if (EqualityComparer.Default.Equals(displayedCount, value))
return;
+
displayedCount = value;
DisplayedCountSpriteText.Text = FormatCount(value);
}
@@ -61,22 +59,16 @@ namespace osu.Game.Graphics.UserInterface
public abstract void Increment(T amount);
- private float textSize;
-
public float TextSize
{
- get { return textSize; }
- set
- {
- textSize = value;
- DisplayedCountSpriteText.TextSize = value;
- }
+ get => DisplayedCountSpriteText.Font.Size;
+ set => DisplayedCountSpriteText.Font = DisplayedCountSpriteText.Font.With(size: value);
}
public Color4 AccentColour
{
- get { return DisplayedCountSpriteText.Colour; }
- set { DisplayedCountSpriteText.Colour = value; }
+ get => DisplayedCountSpriteText.Colour;
+ set => DisplayedCountSpriteText.Colour = value;
}
///
@@ -86,20 +78,17 @@ namespace osu.Game.Graphics.UserInterface
{
Children = new Drawable[]
{
- DisplayedCountSpriteText = new OsuSpriteText
- {
- Font = @"Venera"
- },
+ DisplayedCountSpriteText = new OsuSpriteText { Font = OsuFont.Numeric }
};
TextSize = 40;
AutoSizeAxes = Axes.Both;
- DisplayedCount = Current;
+ DisplayedCount = Current.Value;
- Current.ValueChanged += newValue =>
+ Current.ValueChanged += val =>
{
- if (IsLoaded) TransformCount(displayedCount, newValue);
+ if (IsLoaded) TransformCount(displayedCount, val.NewValue);
};
}
@@ -107,7 +96,7 @@ namespace osu.Game.Graphics.UserInterface
{
base.LoadComplete();
- DisplayedCountSpriteText.Text = FormatCount(Current);
+ DisplayedCountSpriteText.Text = FormatCount(Current.Value);
}
///
@@ -126,7 +115,7 @@ namespace osu.Game.Graphics.UserInterface
public virtual void StopRolling()
{
FinishTransforms(false, nameof(DisplayedCount));
- DisplayedCount = Current;
+ DisplayedCount = Current.Value;
}
///
diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs
index 944993d99c..63062cdc9d 100644
--- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs
+++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface
/// How many leading zeroes the counter will have.
public ScoreCounter(uint leading = 0)
{
- DisplayedCountSpriteText.FixedWidth = true;
+ DisplayedCountSpriteText.Font = DisplayedCountSpriteText.Font.With(fixedWidth: true);
LeadingZeroes = leading;
}
@@ -52,7 +52,7 @@ namespace osu.Game.Graphics.UserInterface
public override void Increment(double amount)
{
- Current.Value = Current + amount;
+ Current.Value = Current.Value + amount;
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs
index f7c907c5ed..f564a4b5a8 100644
--- a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs
+++ b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs
@@ -10,45 +10,30 @@ namespace osu.Game.Graphics.UserInterface
///
/// A which follows the active screen (and allows navigation) in a stack.
///
- public class ScreenBreadcrumbControl : BreadcrumbControl
+ public class ScreenBreadcrumbControl : BreadcrumbControl
{
- private Screen last;
-
- public ScreenBreadcrumbControl(Screen initialScreen)
+ public ScreenBreadcrumbControl(ScreenStack stack)
{
- Current.ValueChanged += newScreen =>
- {
- if (last != newScreen && !newScreen.IsCurrentScreen)
- newScreen.MakeCurrent();
- };
+ stack.ScreenPushed += onPushed;
+ stack.ScreenExited += onExited;
- onPushed(initialScreen);
+ onPushed(null, stack.CurrentScreen);
+
+ Current.ValueChanged += current => current.NewValue.MakeCurrent();
}
- private void screenChanged(Screen newScreen)
+ private void onPushed(IScreen lastScreen, IScreen newScreen)
{
- if (newScreen == null) return;
-
- if (last != null)
- {
- last.Exited -= screenChanged;
- last.ModePushed -= onPushed;
- }
-
- last = newScreen;
-
- newScreen.Exited += screenChanged;
- newScreen.ModePushed += onPushed;
-
+ AddItem(newScreen);
Current.Value = newScreen;
}
- private void onPushed(Screen screen)
+ private void onExited(IScreen lastScreen, IScreen newScreen)
{
- Items.ToList().SkipWhile(i => i != Current.Value).Skip(1).ForEach(RemoveItem);
- AddItem(screen);
+ if (newScreen != null)
+ Current.Value = newScreen;
- screenChanged(screen);
+ Items.ToList().SkipWhile(s => s != Current.Value).Skip(1).ForEach(RemoveItem);
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs b/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs
index b0657e707e..4717401c75 100644
--- a/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs
+++ b/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Graphics.UserInterface
public override void Increment(int amount)
{
- Current.Value = Current + amount;
+ Current.Value = Current.Value + amount;
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs
index ad8ff8ec74..7dc05d174f 100644
--- a/osu.Game/Graphics/UserInterface/StarCounter.cs
+++ b/osu.Game/Graphics/UserInterface/StarCounter.cs
@@ -41,10 +41,7 @@ namespace osu.Game.Graphics.UserInterface
///
public float CountStars
{
- get
- {
- return countStars;
- }
+ get => countStars;
set
{
@@ -137,6 +134,7 @@ namespace osu.Game.Graphics.UserInterface
private class Star : Container
{
public readonly SpriteIcon Icon;
+
public Star()
{
Size = new Vector2(star_size);
diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs
index 31fe29fc3a..685d230a4b 100644
--- a/osu.Game/Graphics/UserInterface/TriangleButton.cs
+++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs
@@ -27,14 +27,11 @@ namespace osu.Game.Graphics.UserInterface
});
}
- public IEnumerable FilterTerms => new[] { Text };
+ public virtual IEnumerable FilterTerms => new[] { Text };
public bool MatchingFilter
{
- set
- {
- this.FadeTo(value ? 1 : 0);
- }
+ set => this.FadeTo(value ? 1 : 0);
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
index eddacf8e2d..1d8298904b 100644
--- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
+++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
@@ -48,11 +48,7 @@ namespace osu.Game.Graphics.UserInterface
public override Anchor Origin
{
- get
- {
- return base.Origin;
- }
-
+ get => base.Origin;
set
{
base.Origin = value;
@@ -155,18 +151,12 @@ namespace osu.Game.Graphics.UserInterface
public FontAwesome Icon
{
- set
- {
- bouncingIcon.Icon = value;
- }
+ set => bouncingIcon.Icon = value;
}
public string Text
{
- set
- {
- text.Text = value;
- }
+ set => text.Text = value;
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => IconLayer.ReceivePositionalInputAt(screenSpacePos) || TextLayer.ReceivePositionalInputAt(screenSpacePos);
@@ -217,7 +207,10 @@ namespace osu.Game.Graphics.UserInterface
private readonly SpriteIcon icon;
- public FontAwesome Icon { set { icon.Icon = value; } }
+ public FontAwesome Icon
+ {
+ set => icon.Icon = value;
+ }
public BouncingIcon()
{
diff --git a/osu.Game/IO/FileStore.cs b/osu.Game/IO/FileStore.cs
index 21639e4f43..458f8964f9 100644
--- a/osu.Game/IO/FileStore.cs
+++ b/osu.Game/IO/FileStore.cs
@@ -21,7 +21,8 @@ namespace osu.Game.IO
public new Storage Storage => base.Storage;
- public FileStore(IDatabaseContextFactory contextFactory, Storage storage) : base(contextFactory, storage.GetStorageForDirectory(@"files"))
+ public FileStore(IDatabaseContextFactory contextFactory, Storage storage)
+ : base(contextFactory, storage.GetStorageForDirectory(@"files"))
{
Store = new StorageBackedResourceStore(Storage);
}
diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs
index aba9c289fa..95ee5aea6b 100644
--- a/osu.Game/IO/Legacy/SerializationReader.cs
+++ b/osu.Game/IO/Legacy/SerializationReader.cs
@@ -39,6 +39,7 @@ namespace osu.Game.IO.Legacy
public override string ReadString()
{
if (ReadByte() == 0) return null;
+
return base.ReadString();
}
@@ -48,6 +49,7 @@ namespace osu.Game.IO.Legacy
int len = ReadInt32();
if (len > 0) return ReadBytes(len);
if (len < 0) return null;
+
return Array.Empty();
}
@@ -57,6 +59,7 @@ namespace osu.Game.IO.Legacy
int len = ReadInt32();
if (len > 0) return ReadChars(len);
if (len < 0) return null;
+
return Array.Empty();
}
@@ -65,6 +68,7 @@ namespace osu.Game.IO.Legacy
{
long ticks = ReadInt64();
if (ticks < 0) throw new IOException("Bad ticks count read!");
+
return new DateTime(ticks, DateTimeKind.Utc);
}
@@ -73,6 +77,7 @@ namespace osu.Game.IO.Legacy
{
int count = ReadInt32();
if (count < 0) return null;
+
IList d = new List(count);
SerializationReader sr = new SerializationReader(BaseStream);
@@ -88,6 +93,7 @@ namespace osu.Game.IO.Legacy
{
if (skipErrors)
continue;
+
throw;
}
@@ -102,6 +108,7 @@ namespace osu.Game.IO.Legacy
{
int count = ReadInt32();
if (count < 0) return null;
+
IList d = new List(count);
for (int i = 0; i < count; i++) d.Add((T)ReadObject());
return d;
@@ -112,6 +119,7 @@ namespace osu.Game.IO.Legacy
{
int count = ReadInt32();
if (count < 0) return null;
+
IDictionary d = new Dictionary();
for (int i = 0; i < count; i++) d[(T)ReadObject()] = (U)ReadObject();
return d;
@@ -174,7 +182,7 @@ namespace osu.Game.IO.Legacy
versionBinder = new VersionConfigToNamespaceAssemblyObjectBinder();
formatter = new BinaryFormatter
{
-// AssemblyFormat = FormatterAssemblyStyle.Simple,
+ // AssemblyFormat = FormatterAssemblyStyle.Simple,
Binder = versionBinder
};
}
@@ -224,6 +232,7 @@ namespace osu.Game.IO.Legacy
genType = BindToType(assemblyName, typ);
}
}
+
if (genType != null && tmpTypes.Count > 0)
{
return genType.MakeGenericType(tmpTypes.ToArray());
diff --git a/osu.Game/IO/Legacy/SerializationWriter.cs b/osu.Game/IO/Legacy/SerializationWriter.cs
index be6e9a0afe..695767c822 100644
--- a/osu.Game/IO/Legacy/SerializationWriter.cs
+++ b/osu.Game/IO/Legacy/SerializationWriter.cs
@@ -8,6 +8,7 @@ using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
+
// ReSharper disable ConditionIsAlwaysTrueOrFalse (we're allowing nulls to be passed to the writer where the underlying class doesn't).
// ReSharper disable HeuristicUnreachableCode
@@ -219,7 +220,7 @@ namespace osu.Game.IO.Legacy
Write((byte)ObjType.otherType);
BinaryFormatter b = new BinaryFormatter
{
-// AssemblyFormat = FormatterAssemblyStyle.Simple,
+ // AssemblyFormat = FormatterAssemblyStyle.Simple,
TypeFormat = FormatterTypeStyle.TypesWhenNeeded
};
b.Serialize(BaseStream, obj);
diff --git a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs
index 5e15b07b46..8c0072c3da 100644
--- a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs
+++ b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs
@@ -19,15 +19,15 @@ namespace osu.Game.Input.Bindings
[Column("Keys")]
public string KeysString
{
- get { return KeyCombination.ToString(); }
- private set { KeyCombination = value; }
+ get => KeyCombination.ToString();
+ private set => KeyCombination = value;
}
[Column("Action")]
public int IntAction
{
- get { return (int)Action; }
- set { Action = value; }
+ get => (int)Action;
+ set => Action = value;
}
}
}
diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs
index 844b6ea658..97f4a9771f 100644
--- a/osu.Game/Input/Bindings/GlobalActionContainer.cs
+++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs
@@ -62,31 +62,41 @@ namespace osu.Game.Input.Bindings
{
[Description("Toggle chat overlay")]
ToggleChat,
+
[Description("Toggle social overlay")]
ToggleSocial,
+
[Description("Reset input settings")]
ResetInputSettings,
+
[Description("Toggle toolbar")]
ToggleToolbar,
+
[Description("Toggle settings")]
ToggleSettings,
+
[Description("Toggle osu!direct")]
ToggleDirect,
+
[Description("Increase volume")]
IncreaseVolume,
+
[Description("Decrease volume")]
DecreaseVolume,
+
[Description("Toggle mute")]
ToggleMute,
// In-Game Keybindings
[Description("Skip cutscene")]
SkipCutscene,
+
[Description("Quick retry (hold)")]
QuickRetry,
[Description("Take screenshot")]
TakeScreenshot,
+
[Description("Toggle gameplay mouse buttons")]
ToggleGameplayMouseButtons,
diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs
index ab010a9532..91e2456ef7 100644
--- a/osu.Game/Input/IdleTracker.cs
+++ b/osu.Game/Input/IdleTracker.cs
@@ -1,7 +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.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
diff --git a/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs b/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs
new file mode 100644
index 0000000000..8e1e3a59f3
--- /dev/null
+++ b/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs
@@ -0,0 +1,487 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using osu.Game.Database;
+
+namespace osu.Game.Migrations
+{
+ [DbContext(typeof(OsuDbContext))]
+ [Migration("20190225062029_AddUserIDColumn")]
+ partial class AddUserIDColumn
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.2.1-servicing-10028");
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ApproachRate");
+
+ b.Property("CircleSize");
+
+ b.Property("DrainRate");
+
+ b.Property("OverallDifficulty");
+
+ b.Property("SliderMultiplier");
+
+ b.Property("SliderTickRate");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapDifficulty");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AudioLeadIn");
+
+ b.Property("BaseDifficultyID");
+
+ b.Property("BeatDivisor");
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("Countdown");
+
+ b.Property("DistanceSpacing");
+
+ b.Property("GridSize");
+
+ b.Property("Hash");
+
+ b.Property("Hidden");
+
+ b.Property("LetterboxInBreaks");
+
+ b.Property("MD5Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapID");
+
+ b.Property("Path");
+
+ b.Property("RulesetID");
+
+ b.Property("SpecialStyle");
+
+ b.Property("StackLeniency");
+
+ b.Property("StarDifficulty");
+
+ b.Property("Status");
+
+ b.Property("StoredBookmarks");
+
+ b.Property("TimelineZoom");
+
+ b.Property("Version");
+
+ b.Property("WidescreenStoryboard");
+
+ b.HasKey("ID");
+
+ b.HasIndex("BaseDifficultyID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("Hash");
+
+ b.HasIndex("MD5Hash");
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapID")
+ .IsUnique();
+
+ b.HasIndex("RulesetID");
+
+ b.ToTable("BeatmapInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Artist");
+
+ b.Property("ArtistUnicode");
+
+ b.Property("AudioFile");
+
+ b.Property("AuthorString")
+ .HasColumnName("Author");
+
+ b.Property("BackgroundFile");
+
+ b.Property("PreviewTime");
+
+ b.Property("Source");
+
+ b.Property("Tags");
+
+ b.Property("Title");
+
+ b.Property("TitleUnicode");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapMetadata");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.HasKey("ID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("FileInfoID");
+
+ b.ToTable("BeatmapSetFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapSetID");
+
+ b.Property("Protected");
+
+ b.Property("Status");
+
+ b.HasKey("ID");
+
+ b.HasIndex("DeletePending");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapSetID")
+ .IsUnique();
+
+ b.ToTable("BeatmapSetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntKey")
+ .HasColumnName("Key");
+
+ b.Property("RulesetID");
+
+ b.Property("StringValue")
+ .HasColumnName("Value");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("Settings");
+ });
+
+ modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Hash");
+
+ b.Property("ReferenceCount");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("ReferenceCount");
+
+ b.ToTable("FileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntAction")
+ .HasColumnName("Action");
+
+ b.Property("KeysString")
+ .HasColumnName("Keys");
+
+ b.Property("RulesetID");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("IntAction");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("KeyBinding");
+ });
+
+ modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Available");
+
+ b.Property("InstantiationInfo");
+
+ b.Property("Name");
+
+ b.Property("ShortName");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Available");
+
+ b.HasIndex("ShortName")
+ .IsUnique();
+
+ b.ToTable("RulesetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.Property("ScoreInfoID");
+
+ b.HasKey("ID");
+
+ b.HasIndex("FileInfoID");
+
+ b.HasIndex("ScoreInfoID");
+
+ b.ToTable("ScoreFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Accuracy")
+ .HasColumnType("DECIMAL(1,4)");
+
+ b.Property("BeatmapInfoID");
+
+ b.Property("Combo");
+
+ b.Property("Date");
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("MaxCombo");
+
+ b.Property("ModsJson")
+ .HasColumnName("Mods");
+
+ b.Property("OnlineScoreID");
+
+ b.Property("PP");
+
+ b.Property("Rank");
+
+ b.Property("RulesetID");
+
+ b.Property("StatisticsJson")
+ .HasColumnName("Statistics");
+
+ b.Property("TotalScore");
+
+ b.Property("UserID")
+ .HasColumnName("UserID");
+
+ b.Property("UserString")
+ .HasColumnName("User");
+
+ b.HasKey("ID");
+
+ b.HasIndex("BeatmapInfoID");
+
+ b.HasIndex("OnlineScoreID")
+ .IsUnique();
+
+ b.HasIndex("RulesetID");
+
+ b.ToTable("ScoreInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.Property("SkinInfoID");
+
+ b.HasKey("ID");
+
+ b.HasIndex("FileInfoID");
+
+ b.HasIndex("SkinInfoID");
+
+ b.ToTable("SkinFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Creator");
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("Name");
+
+ b.HasKey("ID");
+
+ b.HasIndex("DeletePending");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.ToTable("SkinInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
+ .WithMany()
+ .HasForeignKey("BaseDifficultyID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
+ .WithMany("Beatmaps")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("Beatmaps")
+ .HasForeignKey("MetadataID");
+
+ b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
+ .WithMany()
+ .HasForeignKey("RulesetID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
+ .WithMany("Files")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("BeatmapSets")
+ .HasForeignKey("MetadataID");
+ });
+
+ modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
+ {
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Scoring.ScoreInfo")
+ .WithMany("Files")
+ .HasForeignKey("ScoreInfoID");
+ });
+
+ modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
+ .WithMany("Scores")
+ .HasForeignKey("BeatmapInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
+ .WithMany()
+ .HasForeignKey("RulesetID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Skinning.SkinInfo")
+ .WithMany("Files")
+ .HasForeignKey("SkinInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs b/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs
new file mode 100644
index 0000000000..0720e0eac7
--- /dev/null
+++ b/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs
@@ -0,0 +1,22 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace osu.Game.Migrations
+{
+ public partial class AddUserIDColumn : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "UserID",
+ table: "ScoreInfo",
+ nullable: true);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "UserID",
+ table: "ScoreInfo");
+ }
+ }
+}
diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
index 2dafedc3ac..8430e00e4f 100644
--- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
+++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Migrations
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
+ .HasAnnotation("ProductVersion", "2.2.1-servicing-10028");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
@@ -336,7 +336,10 @@ namespace osu.Game.Migrations
b.Property("StatisticsJson")
.HasColumnName("Statistics");
- b.Property("TotalScore");
+ b.Property("TotalScore");
+
+ b.Property("UserID")
+ .HasColumnName("UserID");
b.Property("UserString")
.HasColumnName("User");
diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs
index 9f5eb1441c..4d039e0b8a 100644
--- a/osu.Game/Online/API/APIAccess.cs
+++ b/osu.Game/Online/API/APIAccess.cs
@@ -8,7 +8,7 @@ using System.Net;
using System.Net.Http;
using System.Threading;
using Newtonsoft.Json.Linq;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Game.Configuration;
@@ -64,7 +64,7 @@ namespace osu.Game.Online.API
thread.Start();
}
- private void onTokenChanged(OAuthToken token) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty);
+ private void onTokenChanged(ValueChangedEvent e) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty);
private readonly List components = new List();
@@ -176,6 +176,7 @@ namespace osu.Game.Online.API
lock (queue)
{
if (queue.Count == 0) break;
+
req = queue.Dequeue();
}
@@ -260,7 +261,7 @@ namespace osu.Game.Online.API
public APIState State
{
- get { return state; }
+ get => state;
private set
{
APIState oldState = state;
@@ -355,11 +356,7 @@ namespace osu.Game.Online.API
State = APIState.Offline;
}
- private static User createGuestUser() => new User
- {
- Username = @"Guest",
- Id = 1,
- };
+ private static User createGuestUser() => new GuestUser();
protected override void Dispose(bool isDisposing)
{
@@ -370,6 +367,15 @@ namespace osu.Game.Online.API
}
}
+ internal class GuestUser : User
+ {
+ public GuestUser()
+ {
+ Username = @"Guest";
+ Id = 1;
+ }
+ }
+
public enum APIState
{
///
diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs
index b9449b57f2..efc832a71e 100644
--- a/osu.Game/Online/API/APIDownloadRequest.cs
+++ b/osu.Game/Online/API/APIDownloadRequest.cs
@@ -1,20 +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 System.IO;
using osu.Framework.IO.Network;
namespace osu.Game.Online.API
{
public abstract class APIDownloadRequest : APIRequest
{
+ private string filename;
+
protected override WebRequest CreateWebRequest()
{
- var request = new WebRequest(Uri);
+ var request = new FileWebRequest(filename = Path.GetTempFileName(), Uri);
request.DownloadProgress += request_Progress;
return request;
}
- private void request_Progress(long current, long total) => API.Schedule(() => Progress?.Invoke(current, total));
+ private void request_Progress(long current, long total) => API.Schedule(() => Progressed?.Invoke(current, total));
protected APIDownloadRequest()
{
@@ -23,11 +26,11 @@ namespace osu.Game.Online.API
private void onSuccess()
{
- Success?.Invoke(WebRequest.ResponseData);
+ Success?.Invoke(filename);
}
- public event APIProgressHandler Progress;
+ public event APIProgressHandler Progressed;
- public new event APISuccessHandler Success;
+ public new event APISuccessHandler Success;
}
}
diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs
index 59cd685295..2781a5709b 100644
--- a/osu.Game/Online/API/APIRequest.cs
+++ b/osu.Game/Online/API/APIRequest.cs
@@ -121,7 +121,10 @@ namespace osu.Game.Online.API
}
public delegate void APIFailureHandler(Exception e);
+
public delegate void APISuccessHandler();
+
public delegate void APIProgressHandler(long current, long total);
+
public delegate void APISuccessHandler(T content);
}
diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs
index 600192cb85..096ab5d8c8 100644
--- a/osu.Game/Online/API/DummyAPIAccess.cs
+++ b/osu.Game/Online/API/DummyAPIAccess.cs
@@ -1,7 +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.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Users;
namespace osu.Game.Online.API
diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs
index ca60506da9..e4533ecb3d 100644
--- a/osu.Game/Online/API/IAPIProvider.cs
+++ b/osu.Game/Online/API/IAPIProvider.cs
@@ -1,7 +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.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Game.Users;
namespace osu.Game.Online.API
diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs
index ffe63e68ea..baf494ebf9 100644
--- a/osu.Game/Online/API/OAuth.cs
+++ b/osu.Game/Online/API/OAuth.cs
@@ -3,7 +3,7 @@
using System.Diagnostics;
using System.Net.Http;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.IO.Network;
namespace osu.Game.Online.API
diff --git a/osu.Game/Online/API/OAuthToken.cs b/osu.Game/Online/API/OAuthToken.cs
index 4c9c7b808f..f103d0917d 100644
--- a/osu.Game/Online/API/OAuthToken.cs
+++ b/osu.Game/Online/API/OAuthToken.cs
@@ -19,15 +19,8 @@ namespace osu.Game.Online.API
[JsonProperty(@"expires_in")]
public long ExpiresIn
{
- get
- {
- return AccessTokenExpiry - DateTimeOffset.UtcNow.ToUnixTimeSeconds();
- }
-
- set
- {
- AccessTokenExpiry = DateTimeOffset.Now.AddSeconds(value).ToUnixTimeSeconds();
- }
+ get => AccessTokenExpiry - DateTimeOffset.UtcNow.ToUnixTimeSeconds();
+ set => AccessTokenExpiry = DateTimeOffset.Now.AddSeconds(value).ToUnixTimeSeconds();
}
public bool IsValid => !string.IsNullOrEmpty(AccessToken) && ExpiresIn > 30;
@@ -57,6 +50,7 @@ namespace osu.Game.Online.API
catch
{
}
+
return null;
}
}
diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
index 5e870a5c8f..26e8acc2fc 100644
--- a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
+++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
@@ -10,7 +10,9 @@ namespace osu.Game.Online.API.Requests
{
public readonly BeatmapSetInfo BeatmapSet;
- public Action DownloadProgressed;
+ public float Progress;
+
+ public event Action DownloadProgressed;
private readonly bool noVideo;
@@ -19,7 +21,7 @@ namespace osu.Game.Online.API.Requests
this.noVideo = noVideo;
BeatmapSet = set;
- Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
+ Progressed += (current, total) => DownloadProgressed?.Invoke(Progress = (float)current / total);
}
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";
diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
index 01c91ca780..d9d05ff285 100644
--- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
+++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
@@ -36,11 +36,12 @@ namespace osu.Game.Online.API.Requests
[Description("Ranked & Approved")]
RankedApproved = 0,
- Approved = 1,
+ Qualified = 3,
Loved = 8,
Favourites = 2,
- Qualified = 3,
- Pending = 4,
+
+ [Description("Pending & WIP")]
+ PendingWIP = 4,
Graveyard = 5,
[Description("My Maps")]
diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs
index c60c1e44ee..9ec39c5cb1 100644
--- a/osu.Game/Online/Chat/Channel.cs
+++ b/osu.Game/Online/Chat/Channel.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Newtonsoft.Json;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Lists;
using osu.Game.Users;
diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index ded7920cc1..8c6422afe3 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Logging;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
@@ -55,7 +55,7 @@ namespace osu.Game.Online.Chat
{
CurrentChannel.ValueChanged += currentChannelChanged;
- HighPollRate.BindValueChanged(high => TimeBetweenPolls = high ? 1000 : 6000, true);
+ HighPollRate.BindValueChanged(enabled => TimeBetweenPolls = enabled.NewValue ? 1000 : 6000, true);
}
///
@@ -84,7 +84,7 @@ namespace osu.Game.Online.Chat
?? new Channel(user);
}
- private void currentChannelChanged(Channel channel) => JoinChannel(channel);
+ private void currentChannelChanged(ValueChangedEvent e) => JoinChannel(e.NewValue);
///
/// Ensure we run post actions in sequence, once at a time.
@@ -131,7 +131,7 @@ namespace osu.Game.Online.Chat
target.AddLocalEcho(message);
// if this is a PM and the first message, we need to do a special request to create the PM channel
- if (target.Type == ChannelType.PM && !target.Joined)
+ if (target.Type == ChannelType.PM && !target.Joined.Value)
{
var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(target.Users.First(), message);
@@ -331,7 +331,7 @@ namespace osu.Game.Online.Chat
switch (channel.Type)
{
case ChannelType.Public:
- var req = new JoinChannelRequest(channel, api.LocalUser);
+ var req = new JoinChannelRequest(channel, api.LocalUser.Value);
req.Success += () => JoinChannel(channel, true);
req.Failure += ex => LeaveChannel(channel);
api.Queue(req);
@@ -363,7 +363,7 @@ namespace osu.Game.Online.Chat
if (channel.Joined.Value)
{
- api.Queue(new LeaveChannelRequest(channel, api.LocalUser));
+ api.Queue(new LeaveChannelRequest(channel, api.LocalUser.Value));
channel.Joined.Value = false;
}
}
diff --git a/osu.Game/Online/Chat/ErrorMessage.cs b/osu.Game/Online/Chat/ErrorMessage.cs
index 46e222f11d..a8ff0e9a98 100644
--- a/osu.Game/Online/Chat/ErrorMessage.cs
+++ b/osu.Game/Online/Chat/ErrorMessage.cs
@@ -5,7 +5,8 @@ namespace osu.Game.Online.Chat
{
public class ErrorMessage : InfoMessage
{
- public ErrorMessage(string message) : base(message)
+ public ErrorMessage(string message)
+ : base(message)
{
Sender.Colour = @"ff0000";
}
diff --git a/osu.Game/Online/Chat/ExternalLinkOpener.cs b/osu.Game/Online/Chat/ExternalLinkOpener.cs
index a2c5a3cf8c..495f1ac0b0 100644
--- a/osu.Game/Online/Chat/ExternalLinkOpener.cs
+++ b/osu.Game/Online/Chat/ExternalLinkOpener.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Game.Configuration;
@@ -27,7 +27,7 @@ namespace osu.Game.Online.Chat
public void OpenUrlExternally(string url)
{
- if (externalLinkWarning)
+ if (externalLinkWarning.Value)
dialogOverlay.Push(new ExternalLinkDialog(url, () => host.OpenUrlExternally(url)));
else
host.OpenUrlExternally(url);
diff --git a/osu.Game/Online/Chat/InfoMessage.cs b/osu.Game/Online/Chat/InfoMessage.cs
index 4ef0d5ec65..8dce188804 100644
--- a/osu.Game/Online/Chat/InfoMessage.cs
+++ b/osu.Game/Online/Chat/InfoMessage.cs
@@ -10,7 +10,8 @@ namespace osu.Game.Online.Chat
{
private static int infoID = -1;
- public InfoMessage(string message) : base(infoID--)
+ public InfoMessage(string message)
+ : base(infoID--)
{
Timestamp = DateTimeOffset.Now;
Content = message;
diff --git a/osu.Game/Online/Chat/LocalEchoMessage.cs b/osu.Game/Online/Chat/LocalEchoMessage.cs
index 639509851d..8a39515575 100644
--- a/osu.Game/Online/Chat/LocalEchoMessage.cs
+++ b/osu.Game/Online/Chat/LocalEchoMessage.cs
@@ -5,7 +5,8 @@ namespace osu.Game.Online.Chat
{
public class LocalEchoMessage : LocalMessage
{
- public LocalEchoMessage() : base(null)
+ public LocalEchoMessage()
+ : base(null)
{
}
}
diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs
index 726f6ad2f0..d35dc07368 100644
--- a/osu.Game/Online/Chat/MessageFormatter.cs
+++ b/osu.Game/Online/Chat/MessageFormatter.cs
@@ -25,19 +25,19 @@ namespace osu.Game.Online.Chat
// This is in the format (, [optional]):
// http[s]://.[:port][/path][?query][#fragment]
private static readonly Regex advanced_link_regex = new Regex(
- // protocol
- @"(?[a-z]*?:\/\/" +
- // domain + tld
- @"(?(?:[a-z0-9]\.|[a-z0-9][a-z0-9-]*[a-z0-9]\.)*[a-z0-9-]*[a-z0-9]" +
- // port (optional)
- @"(?::\d+)?)" +
- // path (optional)
- @"(?(?:(?:\/+(?:[a-z0-9$_\.\+!\*\',;:\(\)@&~=-]|%[0-9a-f]{2})*)*" +
- // query (optional)
- @"(?:\?(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?" +
- // fragment (optional)
- @"(?:#(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?)",
- RegexOptions.IgnoreCase);
+ // protocol
+ @"(?[a-z]*?:\/\/" +
+ // domain + tld
+ @"(?(?:[a-z0-9]\.|[a-z0-9][a-z0-9-]*[a-z0-9]\.)*[a-z0-9-]*[a-z0-9]" +
+ // port (optional)
+ @"(?::\d+)?)" +
+ // path (optional)
+ @"(?(?:(?:\/+(?:[a-z0-9$_\.\+!\*\',;:\(\)@&~=-]|%[0-9a-f]{2})*)*" +
+ // query (optional)
+ @"(?:\?(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?" +
+ // fragment (optional)
+ @"(?:#(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?)",
+ RegexOptions.IgnoreCase);
// 00:00:000 (1,2,3) - test
private static readonly Regex time_regex = new Regex(@"\d\d:\d\d:\d\d\d? [^-]*");
@@ -56,14 +56,14 @@ namespace osu.Game.Online.Chat
var index = m.Index - captureOffset;
var displayText = string.Format(display,
- m.Groups[0],
- m.Groups.Count > 1 ? m.Groups[1].Value : "",
- m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
+ m.Groups[0],
+ m.Groups.Count > 1 ? m.Groups[1].Value : "",
+ m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
var linkText = string.Format(link,
- m.Groups[0],
- m.Groups.Count > 1 ? m.Groups[1].Value : "",
- m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
+ m.Groups[0],
+ m.Groups.Count > 1 ? m.Groups[1].Value : "",
+ m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
if (displayText.Length == 0 || linkText.Length == 0) continue;
diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs
index a3bcc91e1e..438bf231c4 100644
--- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs
+++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs
@@ -3,7 +3,7 @@
using System;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -90,9 +90,9 @@ namespace osu.Game.Online.Chat
return;
if (text[0] == '/')
- ChannelManager?.PostCommand(text.Substring(1), Channel);
+ ChannelManager?.PostCommand(text.Substring(1), Channel.Value);
else
- ChannelManager?.PostMessage(text, target: Channel);
+ ChannelManager?.PostMessage(text, target: Channel.Value);
textbox.Text = string.Empty;
}
@@ -111,13 +111,13 @@ namespace osu.Game.Online.Chat
protected virtual ChatLine CreateMessage(Message message) => new StandAloneMessage(message);
- private void channelChanged(Channel channel)
+ private void channelChanged(ValueChangedEvent e)
{
drawableChannel?.Expire();
- if (channel == null) return;
+ if (e.NewValue == null) return;
- AddInternal(drawableChannel = new StandAloneDrawableChannel(channel)
+ AddInternal(drawableChannel = new StandAloneDrawableChannel(e.NewValue)
{
CreateChatLineAction = CreateMessage,
Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 }
@@ -126,7 +126,7 @@ namespace osu.Game.Online.Chat
protected class StandAloneDrawableChannel : DrawableChannel
{
- public Func CreateChatLineAction;
+ public Func CreateChatLineAction;
protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m);
@@ -144,7 +144,8 @@ namespace osu.Game.Online.Chat
protected override float HorizontalPadding => 10;
protected override float MessagePadding => 120;
- public StandAloneMessage(Message message) : base(message)
+ public StandAloneMessage(Message message)
+ : base(message)
{
}
}
diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs
index f3e7bb5c34..f5b6e185c7 100644
--- a/osu.Game/Online/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Online/Leaderboards/Leaderboard.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Online.Leaderboards
public IEnumerable Scores
{
- get { return scores; }
+ get => scores;
set
{
scores = value;
@@ -98,7 +98,7 @@ namespace osu.Game.Online.Leaderboards
public TScope Scope
{
- get { return scope; }
+ get => scope;
set
{
if (value.Equals(scope))
@@ -117,7 +117,7 @@ namespace osu.Game.Online.Leaderboards
///
protected PlaceholderState PlaceholderState
{
- get { return placeholderState; }
+ get => placeholderState;
set
{
if (value != PlaceholderState.Successful)
@@ -213,12 +213,6 @@ namespace osu.Game.Online.Leaderboards
pendingUpdateScores?.Cancel();
pendingUpdateScores = Schedule(() =>
{
- if (api?.IsLoggedIn != true)
- {
- PlaceholderState = PlaceholderState.NotLoggedIn;
- return;
- }
-
PlaceholderState = PlaceholderState.Retrieving;
loading.Show();
@@ -231,6 +225,12 @@ namespace osu.Game.Online.Leaderboards
if (getScoresRequest == null)
return;
+ if (api?.IsLoggedIn != true)
+ {
+ PlaceholderState = PlaceholderState.NotLoggedIn;
+ return;
+ }
+
getScoresRequest.Failure += e => Schedule(() =>
{
if (e is OperationCanceledException)
@@ -243,6 +243,11 @@ namespace osu.Game.Online.Leaderboards
});
}
+ ///
+ /// Performs a fetch/refresh of scores to be displayed.
+ ///
+ /// A callback which should be called when fetching is completed. Scheduling is not required.
+ /// An responsible for the fetch operation. This will be queued and performed automatically.
protected abstract APIRequest FetchScores(Action> scoresCallback);
private Placeholder currentPlaceholder;
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 7373924612..34981bf849 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
@@ -75,9 +76,7 @@ namespace osu.Game.Online.Leaderboards
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Font = @"Exo2.0-MediumItalic",
- TextSize = 22,
- // ReSharper disable once ImpureMethodCallOnReadonlyValueField
+ Font = OsuFont.GetFont(size: 22, italics: true),
Text = RankPosition.ToString(),
},
},
@@ -137,8 +136,7 @@ namespace osu.Game.Online.Leaderboards
nameLabel = new OsuSpriteText
{
Text = user.Username,
- Font = @"Exo2.0-BoldItalic",
- TextSize = 23,
+ Font = OsuFont.GetFont(size: 23, weight: FontWeight.Bold, italics: true)
},
new FillFlowContainer
{
@@ -187,7 +185,7 @@ namespace osu.Game.Online.Leaderboards
Spacing = new Vector2(5f, 0f),
Children = new Drawable[]
{
- scoreLabel = new GlowingSpriteText(score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")),
+ scoreLabel = new GlowingSpriteText(score.TotalScore.ToString(@"N0"), OsuFont.Numeric.With(size: 23), Color4.White, OsuColour.FromHex(@"83ccfa")),
RankContainer = new Container
{
Size = new Vector2(40f, 20f),
@@ -275,7 +273,7 @@ namespace osu.Game.Online.Leaderboards
private class GlowingSpriteText : Container
{
- public GlowingSpriteText(string text, string font, int textSize, Color4 textColour, Color4 glowColour)
+ public GlowingSpriteText(string text, FontUsage font, Color4 textColour, Color4 glowColour)
{
AutoSizeAxes = Axes.Both;
@@ -296,9 +294,7 @@ namespace osu.Game.Online.Leaderboards
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Font = font,
- FixedWidth = true,
- TextSize = textSize,
+ Font = font.With(fixedWidth: true),
Text = text,
Colour = glowColour,
Shadow = false,
@@ -309,9 +305,7 @@ namespace osu.Game.Online.Leaderboards
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Font = font,
- FixedWidth = true,
- TextSize = textSize,
+ Font = font.With(fixedWidth: true),
Text = text,
Colour = textColour,
Shadow = false,
@@ -369,7 +363,7 @@ namespace osu.Game.Online.Leaderboards
},
},
},
- new GlowingSpriteText(statistic.Value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa"))
+ new GlowingSpriteText(statistic.Value, OsuFont.GetFont(size: 17, weight: FontWeight.Bold), Color4.White, OsuColour.FromHex(@"83ccfa"))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
diff --git a/osu.Game/Online/Leaderboards/MessagePlaceholder.cs b/osu.Game/Online/Leaderboards/MessagePlaceholder.cs
index 441db0d922..d4256e4a9d 100644
--- a/osu.Game/Online/Leaderboards/MessagePlaceholder.cs
+++ b/osu.Game/Online/Leaderboards/MessagePlaceholder.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Online.Leaderboards
{
AddIcon(FontAwesome.fa_exclamation_circle, cp =>
{
- cp.TextSize = TEXT_SIZE;
+ cp.Font = cp.Font.With(size: TEXT_SIZE);
cp.Padding = new MarginPadding { Right = 10 };
});
diff --git a/osu.Game/Online/Leaderboards/Placeholder.cs b/osu.Game/Online/Leaderboards/Placeholder.cs
index 72f9502753..d38110a9d0 100644
--- a/osu.Game/Online/Leaderboards/Placeholder.cs
+++ b/osu.Game/Online/Leaderboards/Placeholder.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Online.Leaderboards
protected const float TEXT_SIZE = 22;
protected Placeholder()
- : base(cp => cp.TextSize = TEXT_SIZE)
+ : base(cp => cp.Font = cp.Font.With(size: TEXT_SIZE))
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs
index ff3ae240cb..e47d497d94 100644
--- a/osu.Game/Online/Multiplayer/PlaylistItem.cs
+++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs
@@ -1,9 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
-using osu.Framework.Configuration;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
@@ -37,10 +37,10 @@ namespace osu.Game.Online.Multiplayer
public RulesetInfo Ruleset { get; set; }
[JsonIgnore]
- public readonly BindableList AllowedMods = new BindableList();
+ public readonly List AllowedMods = new List();
[JsonIgnore]
- public readonly BindableList RequiredMods = new BindableList();
+ public readonly List RequiredMods = new List();
[JsonProperty("beatmap")]
private APIBeatmap apiBeatmap { get; set; }
diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs
index c20780aecc..77236ce3c8 100644
--- a/osu.Game/Online/Multiplayer/Room.cs
+++ b/osu.Game/Online/Multiplayer/Room.cs
@@ -5,7 +5,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
-using osu.Framework.Configuration;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Game.Online.Multiplayer.GameTypes;
using osu.Game.Online.Multiplayer.RoomStatuses;
using osu.Game.Users;
@@ -14,49 +15,78 @@ namespace osu.Game.Online.Multiplayer
{
public class Room
{
+ [Cached]
[JsonProperty("id")]
public Bindable RoomID { get; private set; } = new Bindable();
+ [Cached]
[JsonProperty("name")]
public Bindable Name { get; private set; } = new Bindable();
+ [Cached]
[JsonProperty("host")]
public Bindable Host { get; private set; } = new Bindable();
+ [Cached]
[JsonProperty("playlist")]
- public BindableList Playlist { get; set; } = new BindableList();
+ public BindableList Playlist { get; private set; } = new BindableList();
+ [Cached]
+ [JsonIgnore]
+ public Bindable CurrentItem { get; private set; } = new Bindable();
+
+ [Cached]
[JsonProperty("channel_id")]
public Bindable ChannelId { get; private set; } = new Bindable();
+ [Cached]
[JsonIgnore]
public Bindable Duration { get; private set; } = new Bindable(TimeSpan.FromMinutes(30));
+ [Cached]
[JsonIgnore]
public Bindable MaxAttempts { get; private set; } = new Bindable();
+ [Cached]
[JsonIgnore]
public Bindable Status { get; private set; } = new Bindable(new RoomStatusOpen());
+ [Cached]
[JsonIgnore]
public Bindable Availability { get; private set; } = new Bindable();
+ [Cached]
[JsonIgnore]
public Bindable Type { get; private set; } = new Bindable(new GameTypeTimeshift());
+ [Cached]
[JsonIgnore]
public Bindable MaxParticipants { get; private set; } = new Bindable();
+ [Cached]
[JsonIgnore]
public Bindable> Participants { get; private set; } = new Bindable>(Enumerable.Empty());
+ [Cached]
public Bindable ParticipantCount { get; private set; } = new Bindable();
+ public Room()
+ {
+ Playlist.ItemsAdded += updateCurrent;
+ Playlist.ItemsRemoved += updateCurrent;
+ updateCurrent(Playlist);
+ }
+
+ private void updateCurrent(IEnumerable playlist)
+ {
+ CurrentItem.Value = playlist.FirstOrDefault();
+ }
+
// todo: TEMPORARY
[JsonProperty("participant_count")]
private int? participantCount
{
- get => ParticipantCount;
+ get => ParticipantCount.Value;
set => ParticipantCount.Value = value ?? 0;
}
@@ -68,6 +98,7 @@ namespace osu.Game.Online.Multiplayer
}
// Only supports retrieval for now
+ [Cached]
[JsonProperty("ends_at")]
public Bindable EndDate { get; private set; } = new Bindable();
@@ -75,7 +106,7 @@ namespace osu.Game.Online.Multiplayer
[JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)]
private int? maxAttempts
{
- get => MaxAttempts;
+ get => MaxAttempts.Value;
set => MaxAttempts.Value = value;
}
@@ -83,23 +114,23 @@ namespace osu.Game.Online.Multiplayer
/// The position of this in the list. This is not read from or written to the API.
///
[JsonIgnore]
- public int Position = -1;
+ public Bindable Position { get; private set; } = new Bindable(-1);
public void CopyFrom(Room other)
{
- RoomID.Value = other.RoomID;
- Name.Value = other.Name;
+ RoomID.Value = other.RoomID.Value;
+ Name.Value = other.Name.Value;
if (other.Host.Value != null && Host.Value?.Id != other.Host.Value.Id)
- Host.Value = other.Host;
+ Host.Value = other.Host.Value;
- Status.Value = other.Status;
- Availability.Value = other.Availability;
- Type.Value = other.Type;
- MaxParticipants.Value = other.MaxParticipants;
+ Status.Value = other.Status.Value;
+ Availability.Value = other.Availability.Value;
+ Type.Value = other.Type.Value;
+ MaxParticipants.Value = other.MaxParticipants.Value;
ParticipantCount.Value = other.ParticipantCount.Value;
Participants.Value = other.Participants.Value.ToArray();
- EndDate.Value = other.EndDate;
+ EndDate.Value = other.EndDate.Value;
if (DateTimeOffset.Now >= EndDate.Value)
Status.Value = new RoomStatusEnded();
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index d8499f4159..cf231f19ce 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using osu.Framework.Configuration;
using osu.Framework.Screens;
using osu.Game.Configuration;
@@ -19,6 +20,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Audio;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
@@ -79,27 +81,23 @@ namespace osu.Game
public virtual Storage GetStorageForStableInstall() => null;
- private Intro intro
- {
- get
- {
- Screen screen = screenStack;
- while (screen != null && !(screen is Intro))
- screen = screen.ChildScreen;
- return screen as Intro;
- }
- }
-
public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight;
private IdleTracker idleTracker;
public readonly Bindable OverlayActivationMode = new Bindable();
- private OsuScreen screenStack;
+ private BackgroundScreenStack backgroundStack;
+ private ParallaxContainer backgroundParallax;
+
+ private ScreenStack screenStack;
private VolumeOverlay volume;
private OnScreenDisplay onscreenDisplay;
+ private OsuLogo osuLogo;
+
+ private MainMenu menuScreen;
+ private Intro introScreen;
private Bindable configRuleset;
private readonly Bindable ruleset = new Bindable();
@@ -112,6 +110,8 @@ namespace osu.Game
private readonly List overlays = new List();
+ private readonly List visibleBlockingOverlays = new List();
+
// todo: move this to SongSelect once Screen has the ability to unsuspend.
[Cached]
[Cached(Type = typeof(IBindable>))]
@@ -130,6 +130,22 @@ namespace osu.Game
public void ToggleDirect() => direct.ToggleVisibility();
+ private void updateBlockingOverlayFade() =>
+ screenContainer.FadeColour(visibleBlockingOverlays.Any() ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint);
+
+ public void AddBlockingOverlay(OverlayContainer overlay)
+ {
+ if (!visibleBlockingOverlays.Contains(overlay))
+ visibleBlockingOverlays.Add(overlay);
+ updateBlockingOverlayFade();
+ }
+
+ public void RemoveBlockingOverlay(OverlayContainer overlay)
+ {
+ visibleBlockingOverlays.Remove(overlay);
+ updateBlockingOverlayFade();
+ }
+
///
/// Close all game-wide overlays.
///
@@ -151,8 +167,6 @@ namespace osu.Game
{
this.frameworkConfig = frameworkConfig;
- ScoreManager.ItemAdded += (score, _, silent) => Schedule(() => LoadScore(score, silent));
-
if (!Host.IsPrimaryInstance)
{
Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error);
@@ -173,18 +187,22 @@ namespace osu.Game
dependencies.CacheAs(ruleset);
dependencies.CacheAs>(ruleset);
+ dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 });
+
// bind config int to database RulesetInfo
configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset);
ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
- ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0;
+ ruleset.ValueChanged += r => configRuleset.Value = r.NewValue.ID ?? 0;
// bind config int to database SkinInfo
configSkin = LocalConfig.GetBindable(OsuSetting.Skin);
- SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID;
- configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == id) ?? SkinInfo.Default;
+ SkinManager.CurrentSkinInfo.ValueChanged += skin => configSkin.Value = skin.NewValue.ID;
+ configSkin.ValueChanged += skinId => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == skinId.NewValue) ?? SkinInfo.Default;
configSkin.TriggerChange();
LocalConfig.BindWith(OsuSetting.VolumeInactive, inactiveVolumeAdjust);
+
+ IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true);
}
private ExternalLinkOpener externalLinkOpener;
@@ -197,58 +215,12 @@ namespace osu.Game
externalLinkOpener.OpenUrlExternally(url);
}
- private ScheduledDelegate scoreLoad;
-
///
/// Show a beatmap set as an overlay.
///
/// The set to display.
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
- ///
- /// Present a beatmap at song select.
- ///
- /// The beatmap to select.
- public void PresentBeatmap(BeatmapSetInfo beatmap)
- {
- CloseAllOverlays(false);
-
- void setBeatmap()
- {
- if (Beatmap.Disabled)
- {
- Schedule(setBeatmap);
- return;
- }
-
- var databasedSet = beatmap.OnlineBeatmapSetID != null ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash);
-
- if (databasedSet != null)
- {
- // Use first beatmap available for current ruleset, else switch ruleset.
- var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
-
- ruleset.Value = first.Ruleset;
- Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
- }
- }
-
- switch (currentScreen)
- {
- case SongSelect _:
- break;
- default:
- // navigate to song select if we are not already there.
- var menu = (MainMenu)intro.ChildScreen;
-
- menu.MakeCurrent();
- menu.LoadToSolo();
- break;
- }
-
- setBeatmap();
- }
-
///
/// Show a user's profile as an overlay.
///
@@ -261,21 +233,44 @@ namespace osu.Game
/// The beatmap to show.
public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId);
- protected void LoadScore(ScoreInfo score, bool silent)
+ ///
+ /// Present a beatmap at song select immediately.
+ /// The user should have already requested this interactively.
+ ///
+ /// The beatmap to select.
+ public void PresentBeatmap(BeatmapSetInfo beatmap)
{
- if (silent)
- return;
+ var databasedSet = beatmap.OnlineBeatmapSetID != null
+ ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID)
+ : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash);
- scoreLoad?.Cancel();
-
- var menu = intro.ChildScreen;
-
- if (menu == null)
+ if (databasedSet == null)
{
- scoreLoad = Schedule(() => LoadScore(score, false));
+ Logger.Log("The requested beatmap could not be loaded.", LoggingTarget.Information);
return;
}
+ performFromMainMenu(() =>
+ {
+ // we might already be at song select, so a check is required before performing the load to solo.
+ if (menuScreen.IsCurrentScreen())
+ menuScreen.LoadToSolo();
+
+ // Use first beatmap available for current ruleset, else switch ruleset.
+ var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
+
+ ruleset.Value = first.Ruleset;
+ Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
+ }, $"load {beatmap}", bypassScreenAllowChecks: true, targetScreen: typeof(PlaySongSelect));
+ }
+
+ ///
+ /// Present a score's replay immediately.
+ /// The user should have already requested this interactively.
+ ///
+ /// The beatmap to select.
+ public void PresentScore(ScoreInfo score)
+ {
var databasedScore = ScoreManager.GetScore(score);
var databasedScoreInfo = databasedScore.ScoreInfo;
if (databasedScore.Replay == null)
@@ -291,14 +286,40 @@ namespace osu.Game
return;
}
- if (!currentScreen.AllowExternalScreenChange)
+ performFromMainMenu(() =>
+ {
+ ruleset.Value = databasedScoreInfo.Ruleset;
+
+ Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
+ Beatmap.Value.Mods.Value = databasedScoreInfo.Mods;
+
+ menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
+ }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
+ }
+
+ private ScheduledDelegate performFromMainMenuTask;
+
+ ///
+ /// Perform an action only after returning to the main menu.
+ /// Eagerly tries to exit the current screen until it succeeds.
+ ///
+ /// The action to perform once we are in the correct state.
+ /// The task name to display in a notification (if we can't immediately reach the main menu state).
+ /// An optional target screen type. If this screen is already current we can immediately perform the action without returning to the menu.
+ /// Whether checking should be bypassed.
+ private void performFromMainMenu(Action action, string taskName, Type targetScreen = null, bool bypassScreenAllowChecks = false)
+ {
+ performFromMainMenuTask?.Cancel();
+
+ // if the current screen does not allow screen changing, give the user an option to try again later.
+ if (!bypassScreenAllowChecks && (screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false)
{
notifications.Post(new SimpleNotification
{
- Text = $"Click here to watch {databasedScoreInfo.User.Username} on {databasedScoreInfo.Beatmap}",
+ Text = $"Click here to {taskName}",
Activated = () =>
{
- loadScore();
+ performFromMainMenu(action, taskName, targetScreen, true);
return true;
}
});
@@ -306,24 +327,26 @@ namespace osu.Game
return;
}
- loadScore();
+ CloseAllOverlays(false);
- void loadScore()
+ // we may already be at the target screen type.
+ if (targetScreen != null && screenStack.CurrentScreen?.GetType() == targetScreen)
{
- if (!menu.IsCurrentScreen)
- {
- menu.MakeCurrent();
- this.Delay(500).Schedule(loadScore, out scoreLoad);
- return;
- }
-
- ruleset.Value = databasedScoreInfo.Ruleset;
-
- Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
- Beatmap.Value.Mods.Value = databasedScoreInfo.Mods;
-
- currentScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
+ action();
+ return;
}
+
+ // all conditions have been met to continue with the action.
+ if (menuScreen?.IsCurrentScreen() == true && !Beatmap.Disabled)
+ {
+ action();
+ return;
+ }
+
+ // menuScreen may not be initialised yet (null check required).
+ menuScreen?.MakeCurrent();
+
+ performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName));
}
protected override void Dispose(bool isDisposing)
@@ -339,7 +362,7 @@ namespace osu.Game
// The next time this is updated is in UpdateAfterChildren, which occurs too late and results
// in the cursor being shown for a few frames during the intro.
// This prevents the cursor from showing until we have a screen with CursorVisible = true
- MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
+ MenuCursorContainer.CanShowCursor = menuScreen?.CursorVisible ?? false;
// todo: all archive managers should be able to be looped here.
SkinManager.PostNotification = n => notifications?.Post(n);
@@ -347,8 +370,12 @@ namespace osu.Game
BeatmapManager.PostNotification = n => notifications?.Post(n);
BeatmapManager.GetStableStorage = GetStorageForStableInstall;
+ BeatmapManager.PresentImport = items => PresentBeatmap(items.First());
- BeatmapManager.PresentBeatmap = PresentBeatmap;
+ ScoreManager.PostNotification = n => notifications?.Post(n);
+ ScoreManager.PresentImport = items => PresentScore(items.First());
+
+ Container logoContainer;
AddRange(new Drawable[]
{
@@ -361,6 +388,16 @@ namespace osu.Game
screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays)
{
RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ backgroundParallax = new ParallaxContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = backgroundStack = new BackgroundScreenStack { RelativeSizeAxes = Axes.Both },
+ },
+ screenStack = new ScreenStack { RelativeSizeAxes = Axes.Both },
+ logoContainer = new Container { RelativeSizeAxes = Axes.Both },
+ }
},
overlayContent = new Container
{
@@ -370,12 +407,17 @@ namespace osu.Game
idleTracker = new GameIdleTracker(6000)
});
- loadComponentSingleFile(screenStack = new Loader(), d =>
+ dependencies.Cache(backgroundStack);
+
+ screenStack.ScreenPushed += screenPushed;
+ screenStack.ScreenExited += screenExited;
+
+ loadComponentSingleFile(osuLogo, logoContainer.Add);
+
+ loadComponentSingleFile(new Loader
{
- screenStack.ModePushed += screenAdded;
- screenStack.Exited += screenRemoved;
- screenContainer.Add(screenStack);
- });
+ RelativeSizeAxes = Axes.Both
+ }, screenStack.Push);
loadComponentSingleFile(Toolbar = new Toolbar
{
@@ -383,7 +425,7 @@ namespace osu.Game
OnHome = delegate
{
CloseAllOverlays(false);
- intro?.ChildScreen?.MakeCurrent();
+ menuScreen?.MakeCurrent();
},
}, floatingOverlayContent.Add);
@@ -461,6 +503,7 @@ namespace osu.Game
overlay.StateChanged += state =>
{
if (state == Visibility.Hidden) return;
+
singleDisplaySideOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
};
}
@@ -474,6 +517,7 @@ namespace osu.Game
overlay.StateChanged += state =>
{
if (state == Visibility.Hidden) return;
+
informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
};
}
@@ -495,9 +539,9 @@ namespace osu.Game
};
}
- OverlayActivationMode.ValueChanged += v =>
+ OverlayActivationMode.ValueChanged += mode =>
{
- if (v != OverlayActivation.All) CloseAllOverlays();
+ if (mode.NewValue != OverlayActivation.All) CloseAllOverlays();
};
void updateScreenOffset()
@@ -574,21 +618,10 @@ namespace osu.Game
}
private Task asyncLoadStream;
- private int visibleOverlayCount;
private void loadComponentSingleFile(T d, Action add)
where T : Drawable
{
- var focused = d as FocusedOverlayContainer;
- if (focused != null)
- {
- focused.StateChanged += s =>
- {
- visibleOverlayCount += s == Visibility.Visible ? 1 : -1;
- screenContainer.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint);
- };
- }
-
// schedule is here to ensure that all component loads are done after LoadComplete is run (and thus all dependencies are cached).
// with some better organisation of LoadComplete to do construction and dependency caching in one step, followed by calls to loadComponentSingleFile,
// we could avoid the need for scheduling altogether.
@@ -605,7 +638,25 @@ namespace osu.Game
try
{
Logger.Log($"Loading {d}...", level: LogLevel.Debug);
- await LoadComponentAsync(d, add);
+
+ // Since this is running in a separate thread, it is possible for OsuGame to be disposed after LoadComponentAsync has been called
+ // throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true
+ Task task = null;
+ var del = new ScheduledDelegate(() => task = LoadComponentAsync(d, add));
+ Scheduler.Add(del);
+
+ // The delegate won't complete if OsuGame has been disposed in the meantime
+ while (!IsDisposed && !del.Completed)
+ await Task.Delay(10);
+
+ // Either we're disposed or the load process has started successfully
+ if (IsDisposed)
+ return;
+
+ Debug.Assert(task != null);
+
+ await task;
+
Logger.Log($"Loaded {d}!", level: LogLevel.Debug);
}
catch (OperationCanceledException)
@@ -617,7 +668,7 @@ namespace osu.Game
public bool OnPressed(GlobalAction action)
{
- if (intro == null) return false;
+ if (introScreen == null) return false;
switch (action)
{
@@ -656,16 +707,12 @@ namespace osu.Game
private readonly BindableDouble inactiveVolumeAdjust = new BindableDouble();
- protected override void OnDeactivated()
+ private void updateActiveState(bool isActive)
{
- base.OnDeactivated();
- Audio.AddAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust);
- }
-
- protected override void OnActivated()
- {
- base.OnActivated();
- Audio.RemoveAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust);
+ if (isActive)
+ Audio.RemoveAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust);
+ else
+ Audio.AddAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust);
}
public bool OnReleased(GlobalAction action) => false;
@@ -674,19 +721,20 @@ namespace osu.Game
private Container floatingOverlayContent;
- private OsuScreen currentScreen;
private FrameworkConfigManager frameworkConfig;
private ScalingContainer screenContainer;
protected override bool OnExiting()
{
- if (screenStack.ChildScreen == null) return false;
+ if (screenStack.CurrentScreen is Loader)
+ return false;
- if (intro == null) return true;
+ if (introScreen == null)
+ return true;
- if (!intro.DidLoadMenu || intro.ChildScreen != null)
+ if (!introScreen.DidLoadMenu || !(screenStack.CurrentScreen is Intro))
{
- Scheduler.Add(intro.MakeCurrent);
+ Scheduler.Add(introScreen.MakeCurrent);
return true;
}
@@ -709,63 +757,47 @@ namespace osu.Game
{
base.UpdateAfterChildren();
- // we only want to apply these restrictions when we are inside a screen stack.
- // the use case for not applying is in visual/unit tests.
- bool applyBeatmapRulesetRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false;
-
- ruleset.Disabled = applyBeatmapRulesetRestrictions;
- Beatmap.Disabled = applyBeatmapRulesetRestrictions;
-
screenContainer.Padding = new MarginPadding { Top = ToolbarOffset };
overlayContent.Padding = new MarginPadding { Top = ToolbarOffset };
- MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
+ MenuCursorContainer.CanShowCursor = (screenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
}
- ///
- /// Sets while ignoring any beatmap.
- ///
- /// The beatmap to set.
- public void ForcefullySetBeatmap(WorkingBeatmap beatmap)
+ protected virtual void ScreenChanged(IScreen current, IScreen newScreen)
{
- var beatmapDisabled = Beatmap.Disabled;
+ switch (newScreen)
+ {
+ case Intro intro:
+ introScreen = intro;
+ break;
+ case MainMenu menu:
+ menuScreen = menu;
+ break;
+ }
- Beatmap.Disabled = false;
- Beatmap.Value = beatmap;
- Beatmap.Disabled = beatmapDisabled;
+ if (newScreen is IOsuScreen newOsuScreen)
+ {
+ backgroundParallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * newOsuScreen.BackgroundParallaxAmount;
+
+ OverlayActivationMode.Value = newOsuScreen.InitialOverlayActivationMode;
+
+ if (newOsuScreen.HideOverlaysOnEnter)
+ CloseAllOverlays();
+ else
+ Toolbar.State = Visibility.Visible;
+ }
}
- ///
- /// Sets while ignoring any ruleset restrictions.
- ///
- /// The beatmap to set.
- public void ForcefullySetRuleset(RulesetInfo ruleset)
+ private void screenPushed(IScreen lastScreen, IScreen newScreen)
{
- var rulesetDisabled = this.ruleset.Disabled;
-
- this.ruleset.Disabled = false;
- this.ruleset.Value = ruleset;
- this.ruleset.Disabled = rulesetDisabled;
- }
-
- protected virtual void ScreenChanged(OsuScreen current, Screen newScreen)
- {
- currentScreen = (OsuScreen)newScreen;
- }
-
- private void screenAdded(Screen newScreen)
- {
- ScreenChanged(currentScreen, newScreen);
+ ScreenChanged(lastScreen, newScreen);
Logger.Log($"Screen changed → {newScreen}");
-
- newScreen.ModePushed += screenAdded;
- newScreen.Exited += screenRemoved;
}
- private void screenRemoved(Screen newScreen)
+ private void screenExited(IScreen lastScreen, IScreen newScreen)
{
- ScreenChanged(currentScreen, newScreen);
- Logger.Log($"Screen changed ← {currentScreen}");
+ ScreenChanged(lastScreen, newScreen);
+ Logger.Log($"Screen changed ← {newScreen}");
if (newScreen == null)
Exit();
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 8154466c4f..643a673faf 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -8,7 +8,7 @@ using System.Linq;
using System.Reflection;
using osu.Framework.Allocation;
using osu.Framework.Audio;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.IO.Stores;
@@ -69,14 +69,15 @@ namespace osu.Game
protected override Container Content => content;
- private OsuBindableBeatmap beatmap;
- protected BindableBeatmap Beatmap => beatmap;
+ private Bindable beatmap;
+
+ protected Bindable Beatmap => beatmap;
private Bindable fpsDisplayVisible;
- protected AssemblyName AssemblyName => Assembly.GetEntryAssembly()?.GetName() ?? new AssemblyName { Version = new Version() };
+ public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version();
- public bool IsDeployedBuild => AssemblyName.Version.Major > 0;
+ public bool IsDeployedBuild => AssemblyVersion.Major > 0;
public string Version
{
@@ -85,8 +86,8 @@ namespace osu.Game
if (!IsDeployedBuild)
return @"local " + (DebugUtils.IsDebug ? @"debug" : @"release");
- var assembly = AssemblyName;
- return $@"{assembly.Version.Major}.{assembly.Version.Minor}.{assembly.Version.Build}";
+ var version = AssemblyVersion;
+ return $@"{version.Major}.{version.Minor}.{version.Build}";
}
}
@@ -155,7 +156,6 @@ namespace osu.Game
dependencies.CacheAs(API);
var defaultBeatmap = new DummyWorkingBeatmap(this);
- beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
@@ -174,8 +174,10 @@ namespace osu.Game
// this adds a global reduction of track volume for the time being.
Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
- dependencies.CacheAs(beatmap);
- dependencies.CacheAs(beatmap);
+ beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio);
+
+ dependencies.CacheAs>(beatmap);
+ dependencies.CacheAs(beatmap);
FileStore.Cleanup();
@@ -207,7 +209,7 @@ namespace osu.Game
// TODO: This is temporary until we reimplement the local FPS display.
// It's just to allow end-users to access the framework FPS display without knowing the shortcut key.
fpsDisplayVisible = LocalConfig.GetBindable(OsuSetting.ShowFpsDisplay);
- fpsDisplayVisible.ValueChanged += val => { FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; };
+ fpsDisplayVisible.ValueChanged += visible => { FrameStatisticsMode = visible.NewValue ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; };
fpsDisplayVisible.TriggerChange();
}
diff --git a/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs b/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs
index 071e1d0ffa..7e2ae405cb 100644
--- a/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs
+++ b/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs
@@ -8,22 +8,22 @@ namespace osu.Game.Overlays.AccountCreation
{
public abstract class AccountCreationScreen : Screen
{
- protected override void OnEntering(Screen last)
+ public override void OnEntering(IScreen last)
{
base.OnEntering(last);
- Content.FadeOut().Delay(200).FadeIn(200);
+ this.FadeOut().Delay(200).FadeIn(200);
}
- protected override void OnResuming(Screen last)
+ public override void OnResuming(IScreen last)
{
base.OnResuming(last);
- Content.FadeIn(200);
+ this.FadeIn(200);
}
- protected override void OnSuspending(Screen next)
+ public override void OnSuspending(IScreen next)
{
base.OnSuspending(next);
- Content.FadeOut(200);
+ this.FadeOut(200);
}
}
}
diff --git a/osu.Game/Overlays/AccountCreation/ErrorTextFlowContainer.cs b/osu.Game/Overlays/AccountCreation/ErrorTextFlowContainer.cs
index bb436dbb2c..87ff4dd398 100644
--- a/osu.Game/Overlays/AccountCreation/ErrorTextFlowContainer.cs
+++ b/osu.Game/Overlays/AccountCreation/ErrorTextFlowContainer.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Overlays.AccountCreation
private readonly List errorDrawables = new List();
public ErrorTextFlowContainer()
- : base(cp => { cp.TextSize = 12; })
+ : base(cp => cp.Font = cp.Font.With(size: 12))
{
}
diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
index b0d08d3c6c..2fc00edbc1 100644
--- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
+++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
@@ -10,6 +10,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
+using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
@@ -38,13 +39,15 @@ namespace osu.Game.Overlays.AccountCreation
private OsuTextBox[] textboxes;
private ProcessingOverlay processingOverlay;
+ private GameHost host;
[BackgroundDependencyLoader]
- private void load(OsuColour colours, APIAccess api)
+ private void load(OsuColour colours, APIAccess api, GameHost host)
{
this.api = api;
+ this.host = host;
- Children = new Drawable[]
+ InternalChildren = new Drawable[]
{
new FillFlowContainer
{
@@ -58,10 +61,10 @@ namespace osu.Game.Overlays.AccountCreation
{
new OsuSpriteText
{
- TextSize = 20,
Margin = new MarginPadding { Vertical = 10 },
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
+ Font = OsuFont.GetFont(size: 20),
Text = "Let's create an account!",
},
usernameTextBox = new OsuTextBox
@@ -126,24 +129,24 @@ namespace osu.Game.Overlays.AccountCreation
usernameDescription.AddText("This will be your public presence. No profanity, no impersonation. Avoid exposing your own personal details, too!");
emailAddressDescription.AddText("Will be used for notifications, account verification and in the case you forget your password. No spam, ever.");
- emailAddressDescription.AddText(" Make sure to get it right!", cp => cp.Font = "Exo2.0-Bold");
+ emailAddressDescription.AddText(" Make sure to get it right!", cp => cp.Font = cp.Font.With(Typeface.Exo, weight: FontWeight.Bold));
passwordDescription.AddText("At least ");
characterCheckText = passwordDescription.AddText("8 characters long");
passwordDescription.AddText(". Choose something long but also something you will remember, like a line from your favourite song.");
- passwordTextBox.Current.ValueChanged += text => { characterCheckText.ForEach(s => s.Colour = text.Length == 0 ? Color4.White : Interpolation.ValueAt(text.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In)); };
+ passwordTextBox.Current.ValueChanged += password => { characterCheckText.ForEach(s => s.Colour = password.NewValue.Length == 0 ? Color4.White : Interpolation.ValueAt(password.NewValue.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In)); };
}
protected override void Update()
{
base.Update();
- if (!textboxes.Any(t => t.HasFocus))
+ if (host?.OnScreenKeyboardOverlapsGameWindow != true && !textboxes.Any(t => t.HasFocus))
focusNextTextbox();
}
- protected override void OnEntering(Screen last)
+ public override void OnEntering(IScreen last)
{
base.OnEntering(last);
processingOverlay.Hide();
diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs
index cec0f1a767..4e2cc1ea00 100644
--- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs
+++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs
@@ -26,12 +26,12 @@ namespace osu.Game.Overlays.AccountCreation
private const string help_centre_url = "/help/wiki/Help_Centre#login";
- protected override void OnEntering(Screen last)
+ public override void OnEntering(IScreen last)
{
if (string.IsNullOrEmpty(api.ProvidedUsername))
{
- Content.FadeOut();
- Push(new ScreenEntry());
+ this.FadeOut();
+ this.Push(new ScreenEntry());
return;
}
@@ -46,7 +46,7 @@ namespace osu.Game.Overlays.AccountCreation
if (string.IsNullOrEmpty(api.ProvidedUsername))
return;
- Children = new Drawable[]
+ InternalChildren = new Drawable[]
{
new Sprite
{
@@ -83,14 +83,13 @@ namespace osu.Game.Overlays.AccountCreation
},
new OsuSpriteText
{
- TextSize = 28,
- Font = "Exo2.0-Light",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = Color4.Red,
+ Font = OsuFont.GetFont(size: 28, weight: FontWeight.Light),
Text = "Warning! 注意!",
},
- multiAccountExplanationText = new OsuTextFlowContainer(cp => { cp.TextSize = 12; })
+ multiAccountExplanationText = new OsuTextFlowContainer(cp => cp.Font = cp.Font.With(size: 12))
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
@@ -104,9 +103,9 @@ namespace osu.Game.Overlays.AccountCreation
new DangerousSettingsButton
{
Text = "I understand. This account isn't for me.",
- Action = () => Push(new ScreenEntry())
+ Action = () => this.Push(new ScreenEntry())
},
- furtherAssistance = new LinkFlowContainer(cp => { cp.TextSize = 12; })
+ furtherAssistance = new LinkFlowContainer(cp => cp.Font = cp.Font.With(size: 12))
{
Margin = new MarginPadding { Top = 20 },
Anchor = Anchor.TopCentre,
diff --git a/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs b/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs
index da59b294e5..f6ddb135ec 100644
--- a/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs
+++ b/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs
@@ -4,6 +4,8 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Screens;
+using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Settings;
using osu.Game.Screens.Menu;
@@ -16,7 +18,7 @@ namespace osu.Game.Overlays.AccountCreation
[BackgroundDependencyLoader]
private void load()
{
- Child = new FillFlowContainer
+ InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
@@ -39,24 +41,23 @@ namespace osu.Game.Overlays.AccountCreation
},
new OsuSpriteText
{
- TextSize = 24,
- Font = "Exo2.0-Light",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
+ Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light),
Text = "New Player Registration",
},
new OsuSpriteText
{
- TextSize = 12,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
+ Font = OsuFont.GetFont(size: 12),
Text = "let's get you started",
},
new SettingsButton
{
Text = "Let's create an account!",
Margin = new MarginPadding { Vertical = 120 },
- Action = () => Push(new ScreenWarning())
+ Action = () => this.Push(new ScreenWarning())
}
}
};
diff --git a/osu.Game/Overlays/AccountCreationOverlay.cs b/osu.Game/Overlays/AccountCreationOverlay.cs
index 718a56a441..bc780538d5 100644
--- a/osu.Game/Overlays/AccountCreationOverlay.cs
+++ b/osu.Game/Overlays/AccountCreationOverlay.cs
@@ -6,6 +6,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
@@ -69,7 +70,10 @@ namespace osu.Game.Overlays
Colour = Color4.Black,
Alpha = 0.9f,
},
- welcomeScreen = new ScreenWelcome(),
+ new ScreenStack(welcomeScreen = new ScreenWelcome())
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
}
}
}
@@ -82,7 +86,7 @@ namespace osu.Game.Overlays
base.PopIn();
this.FadeIn(transition_time, Easing.OutQuint);
- if (welcomeScreen.ChildScreen != null)
+ if (welcomeScreen.GetChildScreen() != null)
welcomeScreen.MakeCurrent();
}
diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
index c9907014ca..18de87e7b4 100644
--- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
+++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
@@ -11,6 +11,8 @@ using osuTK;
using osuTK.Graphics;
using osu.Game.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
@@ -25,10 +27,11 @@ namespace osu.Game.Overlays.BeatmapSet
public BeatmapSetInfo BeatmapSet
{
- get { return beatmapSet; }
+ get => beatmapSet;
set
{
if (value == beatmapSet) return;
+
beatmapSet = value;
updateDisplay();
@@ -47,8 +50,8 @@ namespace osu.Game.Overlays.BeatmapSet
fields.Children = new Drawable[]
{
- new Field("mapped by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"),
- new Field("submitted on", online.Submitted.ToString(@"MMMM d, yyyy"), @"Exo2.0-Bold")
+ new Field("mapped by", BeatmapSet.Metadata.Author.Username, OsuFont.GetFont(weight: FontWeight.Regular, italics: true)),
+ new Field("submitted on", online.Submitted.ToString(@"MMMM d, yyyy"), OsuFont.GetFont(weight: FontWeight.Bold))
{
Margin = new MarginPadding { Top = 5 },
},
@@ -56,11 +59,11 @@ namespace osu.Game.Overlays.BeatmapSet
if (online.Ranked.HasValue)
{
- fields.Add(new Field("ranked on", online.Ranked.Value.ToString(@"MMMM d, yyyy"), @"Exo2.0-Bold"));
+ fields.Add(new Field("ranked on", online.Ranked.Value.ToString(@"MMMM d, yyyy"), OsuFont.GetFont(weight: FontWeight.Bold)));
}
else if (online.LastUpdated.HasValue)
{
- fields.Add(new Field("last updated on", online.LastUpdated.Value.ToString(@"MMMM d, yyyy"), @"Exo2.0-Bold"));
+ fields.Add(new Field("last updated on", online.LastUpdated.Value.ToString(@"MMMM d, yyyy"), OsuFont.GetFont(weight: FontWeight.Bold)));
}
}
@@ -105,7 +108,7 @@ namespace osu.Game.Overlays.BeatmapSet
private class Field : FillFlowContainer
{
- public Field(string first, string second, string secondFont)
+ public Field(string first, string second, FontUsage secondFont)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
@@ -115,13 +118,12 @@ namespace osu.Game.Overlays.BeatmapSet
new OsuSpriteText
{
Text = $"{first} ",
- TextSize = 13,
+ Font = OsuFont.GetFont(size: 13)
},
new OsuSpriteText
{
Text = second,
- TextSize = 13,
- Font = secondFont,
+ Font = secondFont.With(size: 13)
},
};
}
diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs
index af5998bfa1..9ed9875be9 100644
--- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs
+++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs
@@ -21,10 +21,11 @@ namespace osu.Game.Overlays.BeatmapSet
public BeatmapSetInfo BeatmapSet
{
- get { return beatmapSet; }
+ get => beatmapSet;
set
{
if (value == beatmapSet) return;
+
beatmapSet = value;
updateDisplay();
@@ -35,10 +36,11 @@ namespace osu.Game.Overlays.BeatmapSet
public BeatmapInfo Beatmap
{
- get { return beatmap; }
+ get => beatmap;
set
{
if (value == beatmap) return;
+
beatmap = value;
updateDisplay();
@@ -95,8 +97,8 @@ namespace osu.Game.Overlays.BeatmapSet
public string Value
{
- get { return value.Text; }
- set { this.value.Text = value; }
+ get => value.Text;
+ set => this.value.Text = value;
}
public Statistic(FontAwesome icon, string name)
@@ -136,9 +138,8 @@ namespace osu.Game.Overlays.BeatmapSet
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
- TextSize = 13,
- Font = @"Exo2.0-Bold",
Margin = new MarginPadding { Left = 10 },
+ Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold),
},
},
},
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
index c2edaf01ed..55dee904b4 100644
--- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
@@ -5,7 +5,7 @@ using System;
using System.Linq;
using osu.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -33,12 +33,14 @@ namespace osu.Game.Overlays.BeatmapSet
public readonly Bindable Beatmap = new Bindable();
private BeatmapSetInfo beatmapSet;
+
public BeatmapSetInfo BeatmapSet
{
- get { return beatmapSet; }
+ get => beatmapSet;
set
{
if (value == beatmapSet) return;
+
beatmapSet = value;
updateDisplay();
@@ -107,15 +109,13 @@ namespace osu.Game.Overlays.BeatmapSet
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
- TextSize = 20,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold)
},
starRating = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
- TextSize = 13,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold),
Text = "Star Difficulty",
Alpha = 0,
Margin = new MarginPadding { Bottom = 1 },
@@ -140,7 +140,7 @@ namespace osu.Game.Overlays.BeatmapSet
Beatmap.ValueChanged += b =>
{
- showBeatmap(b);
+ showBeatmap(b.NewValue);
updateDifficultyButtons();
};
}
@@ -196,12 +196,14 @@ namespace osu.Game.Overlays.BeatmapSet
public event Action StateChanged;
private DifficultySelectorState state;
+
public DifficultySelectorState State
{
- get { return state; }
+ get => state;
set
{
if (value == state) return;
+
state = value;
StateChanged?.Invoke(State);
@@ -279,9 +281,10 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly OsuSpriteText text;
private int value;
+
public int Value
{
- get { return value; }
+ get => value;
set
{
this.value = value;
@@ -309,8 +312,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
- Font = @"Exo2.0-SemiBoldItalic",
- TextSize = 14,
+ Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true)
},
};
}
diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs
index e90fa4fc95..edd886f0f2 100644
--- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs
@@ -2,108 +2,161 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps;
-using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
+using osu.Game.Overlays.Direct;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet.Buttons
{
- public class DownloadButton : HeaderButton, IHasTooltip
+ public class DownloadButton : DownloadTrackingComposite, IHasTooltip
{
- public string TooltipText => "Download this beatmap";
+ private readonly bool noVideo;
+
+ public string TooltipText => button.Enabled.Value ? "Download this beatmap" : "Login to download";
private readonly IBindable localUser = new Bindable();
- public DownloadButton(BeatmapSetInfo set, bool noVideo = false)
- {
- Width = 120;
+ private ShakeContainer shakeContainer;
+ private HeaderButton button;
- BeatmapSetDownloader downloader;
- Add(new Container
+ public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
+ : base(beatmapSet)
+ {
+ this.noVideo = noVideo;
+
+ Width = 120;
+ RelativeSizeAxes = Axes.Y;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(APIAccess api, BeatmapManager beatmaps)
+ {
+ FillFlowContainer textSprites;
+
+ AddRangeInternal(new Drawable[]
{
- Depth = -1,
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Horizontal = 10 },
- Children = new Drawable[]
+ shakeContainer = new ShakeContainer
{
- downloader = new BeatmapSetDownloader(set, noVideo),
- new FillFlowContainer
+ Depth = -1,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ CornerRadius = 5,
+ Children = new Drawable[]
{
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Vertical,
- Children = new[]
+ button = new HeaderButton { RelativeSizeAxes = Axes.Both },
+ new Container
{
- new OsuSpriteText
+ // cannot nest inside here due to the structure of button (putting things in its own content).
+ // requires framework fix.
+ Padding = new MarginPadding { Horizontal = 10 },
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- Text = "Download",
- TextSize = 13,
- Font = @"Exo2.0-Bold",
- },
- new OsuSpriteText
- {
- Text = set.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty,
- TextSize = 11,
- Font = @"Exo2.0-Bold",
- },
+ textSprites = new FillFlowContainer
+ {
+ Depth = -1,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ AutoSizeAxes = Axes.Both,
+ AutoSizeDuration = 500,
+ AutoSizeEasing = Easing.OutQuint,
+ Direction = FillDirection.Vertical,
+ },
+ new SpriteIcon
+ {
+ Depth = -1,
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Icon = FontAwesome.fa_download,
+ Size = new Vector2(16),
+ Margin = new MarginPadding { Right = 5 },
+ },
+ }
+ },
+ new DownloadProgressBar(BeatmapSet.Value)
+ {
+ Depth = -2,
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
},
- },
- new SpriteIcon
- {
- Anchor = Anchor.CentreRight,
- Origin = Anchor.CentreRight,
- Icon = FontAwesome.fa_download,
- Size = new Vector2(16),
- Margin = new MarginPadding { Right = 5 },
},
},
});
- Action = () =>
+ button.Action = () =>
{
- if (downloader.DownloadState.Value == BeatmapSetDownloader.DownloadStatus.Downloading)
+ if (State.Value != DownloadState.NotDownloaded)
{
- Content.MoveToX(-5, 50, Easing.OutSine).Then()
- .MoveToX(5, 100, Easing.InOutSine).Then()
- .MoveToX(-5, 100, Easing.InOutSine).Then()
- .MoveToX(0, 50, Easing.InSine);
+ shakeContainer.Shake();
return;
}
- downloader.Download();
+ beatmaps.Download(BeatmapSet.Value, noVideo);
};
- downloader.DownloadState.ValueChanged += state =>
+ localUser.BindTo(api.LocalUser);
+ localUser.BindValueChanged(userChanged, true);
+ button.Enabled.BindValueChanged(enabledChanged, true);
+
+ State.BindValueChanged(state =>
{
- switch (state)
+ switch (state.NewValue)
{
- case BeatmapSetDownloader.DownloadStatus.Downloaded:
+ case DownloadState.Downloading:
+ textSprites.Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Text = "Downloading...",
+ Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold)
+ },
+ };
+ break;
+ case DownloadState.Downloaded:
+ textSprites.Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Text = "Importing...",
+ Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold)
+ },
+ };
+ break;
+ case DownloadState.LocallyAvailable:
this.FadeOut(200);
break;
- case BeatmapSetDownloader.DownloadStatus.NotDownloaded:
+ case DownloadState.NotDownloaded:
+ textSprites.Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Text = "Download",
+ Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold)
+ },
+ new OsuSpriteText
+ {
+ Text = BeatmapSet.Value.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty,
+ Font = OsuFont.GetFont(size: 11, weight: FontWeight.Bold)
+ },
+ };
this.FadeIn(200);
break;
}
- };
+ }, true);
}
- [BackgroundDependencyLoader]
- private void load(APIAccess api)
- {
- localUser.BindTo(api.LocalUser);
- Enabled.BindValueChanged(enabledChanged, true);
- }
+ private void userChanged(ValueChangedEvent e) => button.Enabled.Value = !(e.NewValue is GuestUser);
- private void enabledChanged(bool enabled) => this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
+ private void enabledChanged(ValueChangedEvent e) => this.FadeColour(e.NewValue ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs
index b672e7bf07..7824a78a14 100644
--- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -53,9 +53,9 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
},
});
- Favourited.ValueChanged += value =>
+ Favourited.ValueChanged += favourited =>
{
- if (value)
+ if (favourited.NewValue)
{
pink.FadeIn(200);
icon.Icon = FontAwesome.fa_heart;
diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs
index 12b4a7e2d7..8c884e0950 100644
--- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -30,8 +30,8 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
public BeatmapSetInfo BeatmapSet
{
- get { return playButton.BeatmapSet; }
- set { playButton.BeatmapSet = value; }
+ get => playButton.BeatmapSet;
+ set => playButton.BeatmapSet = value;
}
public PreviewButton()
@@ -67,7 +67,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
};
Action = () => playButton.Click();
- Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100);
+ Playing.ValueChanged += playing => progress.FadeTo(playing.NewValue ? 1 : 0, 100);
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs
index 538d327d98..fad5c973b7 100644
--- a/osu.Game/Overlays/BeatmapSet/Details.cs
+++ b/osu.Game/Overlays/BeatmapSet/Details.cs
@@ -25,10 +25,11 @@ namespace osu.Game.Overlays.BeatmapSet
public BeatmapSetInfo BeatmapSet
{
- get { return beatmapSet; }
+ get => beatmapSet;
set
{
if (value == beatmapSet) return;
+
beatmapSet = value;
basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
@@ -39,7 +40,7 @@ namespace osu.Game.Overlays.BeatmapSet
public BeatmapInfo Beatmap
{
- get { return beatmap; }
+ get => beatmap;
set
{
if (value == beatmap) return;
diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs
index 1dde70d1a8..95cf9e9d04 100644
--- a/osu.Game/Overlays/BeatmapSet/Header.cs
+++ b/osu.Game/Overlays/BeatmapSet/Header.cs
@@ -13,12 +13,14 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.BeatmapSet.Buttons;
+using osu.Game.Overlays.Direct;
using osuTK;
using osuTK.Graphics;
+using DownloadButton = osu.Game.Overlays.BeatmapSet.Buttons.DownloadButton;
namespace osu.Game.Overlays.BeatmapSet
{
- public class Header : Container
+ public class Header : DownloadTrackingComposite
{
private const float transition_duration = 200;
private const float tabs_height = 50;
@@ -28,76 +30,23 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Box tabsBg;
private readonly UpdateableBeatmapSetCover cover;
private readonly OsuSpriteText title, artist;
- private readonly Container noVideoButtons;
- private readonly FillFlowContainer videoButtons;
private readonly AuthorInfo author;
- private readonly Container downloadButtonsContainer;
+ private readonly FillFlowContainer downloadButtonsContainer;
private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
public Details Details;
public readonly BeatmapPicker Picker;
- private BeatmapSetInfo beatmapSet;
private readonly FavouriteButton favouriteButton;
- public BeatmapSetInfo BeatmapSet
- {
- get { return beatmapSet; }
- set
- {
- if (value == beatmapSet) return;
- beatmapSet = value;
-
- Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet;
-
- updateDisplay();
- }
- }
-
- private void updateDisplay()
- {
- title.Text = BeatmapSet?.Metadata.Title ?? string.Empty;
- artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty;
- onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
- cover.BeatmapSet = BeatmapSet;
-
- if (BeatmapSet != null)
- {
- downloadButtonsContainer.FadeIn(transition_duration);
- favouriteButton.FadeIn(transition_duration);
-
- if (BeatmapSet.OnlineInfo.HasVideo)
- {
- videoButtons.Children = new[]
- {
- new DownloadButton(BeatmapSet),
- new DownloadButton(BeatmapSet, true),
- };
-
- videoButtons.FadeIn(transition_duration);
- noVideoButtons.FadeOut(transition_duration);
- }
- else
- {
- noVideoButtons.Child = new DownloadButton(BeatmapSet);
-
- noVideoButtons.FadeIn(transition_duration);
- videoButtons.FadeOut(transition_duration);
- }
- }
- else
- {
- downloadButtonsContainer.FadeOut(transition_duration);
- favouriteButton.FadeOut(transition_duration);
- }
- }
-
public Header()
{
ExternalLinkButton externalLink;
+
RelativeSizeAxes = Axes.X;
Height = 400;
Masking = true;
+
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
@@ -105,7 +54,8 @@ namespace osu.Game.Overlays.BeatmapSet
Radius = 3,
Offset = new Vector2(0f, 1f),
};
- Children = new Drawable[]
+
+ InternalChildren = new Drawable[]
{
new Container
{
@@ -165,8 +115,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
title = new OsuSpriteText
{
- Font = @"Exo2.0-BoldItalic",
- TextSize = 37,
+ Font = OsuFont.GetFont(size: 37, weight: FontWeight.Bold, italics: true)
},
externalLink = new ExternalLinkButton
{
@@ -176,11 +125,7 @@ namespace osu.Game.Overlays.BeatmapSet
},
}
},
- artist = new OsuSpriteText
- {
- Font = @"Exo2.0-SemiBoldItalic",
- TextSize = 25,
- },
+ artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.SemiBold, italics: true) },
new Container
{
RelativeSizeAxes = Axes.X,
@@ -196,24 +141,11 @@ namespace osu.Game.Overlays.BeatmapSet
Children = new Drawable[]
{
favouriteButton = new FavouriteButton(),
- downloadButtonsContainer = new Container
+ downloadButtonsContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
- Children = new Drawable[]
- {
- noVideoButtons = new Container
- {
- RelativeSizeAxes = Axes.Both,
- Alpha = 0f,
- },
- videoButtons = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.Both,
- Spacing = new Vector2(buttons_spacing),
- Alpha = 0f,
- },
- },
+ Spacing = new Vector2(buttons_spacing),
},
},
},
@@ -244,15 +176,66 @@ namespace osu.Game.Overlays.BeatmapSet
},
};
- Picker.Beatmap.ValueChanged += b => Details.Beatmap = b;
- Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet?.OnlineBeatmapSetID}#{b?.Ruleset.ShortName}/{b?.OnlineBeatmapID}";
+ Picker.Beatmap.ValueChanged += b => Details.Beatmap = b.NewValue;
+ Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}";
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabsBg.Colour = colours.Gray3;
- updateDisplay();
+
+ State.BindValueChanged(_ => updateDownloadButtons());
+
+ BeatmapSet.BindValueChanged(setInfo =>
+ {
+ Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = setInfo.NewValue;
+
+ title.Text = setInfo.NewValue?.Metadata.Title ?? string.Empty;
+ artist.Text = setInfo.NewValue?.Metadata.Artist ?? string.Empty;
+ onlineStatusPill.Status = setInfo.NewValue?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
+ cover.BeatmapSet = setInfo.NewValue;
+
+ if (setInfo.NewValue != null)
+ {
+ downloadButtonsContainer.FadeIn(transition_duration);
+ favouriteButton.FadeIn(transition_duration);
+ }
+ else
+ {
+ downloadButtonsContainer.FadeOut(transition_duration);
+ favouriteButton.FadeOut(transition_duration);
+ }
+
+ updateDownloadButtons();
+ }, true);
+ }
+
+ private void updateDownloadButtons()
+ {
+ if (BeatmapSet.Value == null) return;
+
+ switch (State.Value)
+ {
+ case DownloadState.LocallyAvailable:
+ // temporary for UX until new design is implemented.
+ downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value)
+ {
+ Width = 50,
+ RelativeSizeAxes = Axes.Y
+ };
+ break;
+ case DownloadState.Downloading:
+ case DownloadState.Downloaded:
+ // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design.
+ downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value);
+ break;
+ default:
+ downloadButtonsContainer.Child = new DownloadButton(BeatmapSet.Value);
+ if (BeatmapSet.Value.OnlineInfo.HasVideo)
+ downloadButtonsContainer.Add(new DownloadButton(BeatmapSet.Value, true));
+ break;
+ }
}
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs
index 3a32abe9cc..4d974a0b63 100644
--- a/osu.Game/Overlays/BeatmapSet/Info.cs
+++ b/osu.Game/Overlays/BeatmapSet/Info.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -21,37 +22,20 @@ namespace osu.Game.Overlays.BeatmapSet
private const float metadata_width = 225;
private const float spacing = 20;
- private readonly MetadataSection source, tags;
private readonly Box successRateBackground;
private readonly SuccessRate successRate;
- private BeatmapSetInfo beatmapSet;
- public BeatmapSetInfo BeatmapSet
- {
- get { return beatmapSet; }
- set
- {
- if (value == beatmapSet) return;
- beatmapSet = value;
-
- updateDisplay();
- }
- }
-
- private void updateDisplay()
- {
- source.Text = BeatmapSet?.Metadata.Source ?? string.Empty;
- tags.Text = BeatmapSet?.Metadata.Tags ?? string.Empty;
- }
+ public readonly Bindable BeatmapSet = new Bindable();
public BeatmapInfo Beatmap
{
- get { return successRate.Beatmap; }
- set { successRate.Beatmap = value; }
+ get => successRate.Beatmap;
+ set => successRate.Beatmap = value;
}
public Info()
{
+ MetadataSection source, tags;
RelativeSizeAxes = Axes.X;
Height = 220;
Masking = true;
@@ -129,14 +113,18 @@ namespace osu.Game.Overlays.BeatmapSet
},
},
};
+
+ BeatmapSet.ValueChanged += b =>
+ {
+ source.Text = b.NewValue?.Metadata.Source ?? string.Empty;
+ tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty;
+ };
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
successRateBackground.Colour = colours.GrayE;
-
- updateDisplay();
}
private class MetadataSection : FillFlowContainer
@@ -156,14 +144,14 @@ namespace osu.Game.Overlays.BeatmapSet
this.FadeIn(transition_duration);
textFlow.Clear();
- textFlow.AddText(value, s => s.TextSize = 14);
+ textFlow.AddText(value, s => s.Font = s.Font.With(size: 14));
}
}
public Color4 TextColour
{
- get { return textFlow.Colour; }
- set { textFlow.Colour = value; }
+ get => textFlow.Colour;
+ set => textFlow.Colour = value;
}
public MetadataSection(string title)
@@ -177,8 +165,7 @@ namespace osu.Game.Overlays.BeatmapSet
header = new OsuSpriteText
{
Text = title,
- Font = @"Exo2.0-Bold",
- TextSize = 14,
+ Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
Shadow = false,
Margin = new MarginPadding { Top = 20 },
},
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs b/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs
index 0eb8b325d3..31af251877 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs
@@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
+using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
@@ -16,12 +17,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private UserProfileOverlay profile;
private User user;
+
public User User
{
- get { return user; }
+ get => user;
set
{
if (user == value) return;
+
user = value;
text.Text = user.Username;
@@ -30,21 +33,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
public float TextSize
{
- set
- {
- if (text.TextSize == value) return;
- text.TextSize = value;
- }
- get { return text.TextSize; }
+ get => text.Font.Size;
+ set => text.Font = text.Font.With(size: value);
}
public ClickableUsername()
{
AutoSizeAxes = Axes.Both;
- Child = text = new OsuSpriteText
- {
- Font = @"Exo2.0-BoldItalic",
- };
+ Child = text = new OsuSpriteText { Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) };
}
[BackgroundDependencyLoader(true)]
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs
index 1f50385adc..c6c8315aeb 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = $"#{index + 1}",
- Font = @"Exo2.0-RegularItalic",
+ Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true),
Margin = new MarginPadding { Left = side_margin }
},
new DrawableFlag(score.User.Country)
@@ -87,17 +87,16 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Text = $@"{score.TotalScore:N0}",
- Font = @"Venera",
+ Font = OsuFont.Numeric.With(fixedWidth: true),
RelativePositionAxes = Axes.X,
X = 0.75f,
- FixedWidth = true,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Text = $@"{score.Accuracy:P2}",
- Font = @"Exo2.0-RegularItalic",
+ Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true),
RelativePositionAxes = Axes.X,
X = 0.85f
},
@@ -106,7 +105,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Text = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}",
- Font = @"Exo2.0-RegularItalic",
+ Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true),
Margin = new MarginPadding { Right = side_margin }
},
};
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
index c9551cf6f8..78e560cdbe 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
@@ -44,12 +44,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly ScoreModsContainer modsContainer;
private APIScoreInfo score;
+
public APIScoreInfo Score
{
- get { return score; }
+ get => score;
set
{
if (score == value) return;
+
score = value;
avatar.User = username.User = score.User;
@@ -117,8 +119,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Anchor = Anchor.TopRight,
Origin = Anchor.BottomRight,
Text = "#1",
- TextSize = 40,
- Font = @"Exo2.0-BoldItalic",
+ Font = OsuFont.GetFont(size: 40, weight: FontWeight.Bold, italics: true),
Y = height / 4,
Margin = new MarginPadding { Right = margin }
},
@@ -208,9 +209,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
if (valueText.Text == value)
return;
+
valueText.Text = value;
}
- get { return valueText.Text; }
+ get => valueText.Text;
}
public InfoColumn(string header)
@@ -222,15 +224,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
headerText = new OsuSpriteText
{
- TextSize = 14,
Text = header,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)
},
- valueText = new OsuSpriteText
- {
- TextSize = 25,
- Font = @"Exo2.0-RegularItalic",
- }
+ valueText = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.Regular, italics: true) }
};
}
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs
index ab34d2ba1d..6c65d491af 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
public IEnumerable Scores
{
- get { return scores; }
+ get => scores;
set
{
getScoresRequest?.Cancel();
diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
index 28902561f4..c89bddca63 100644
--- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
+++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
@@ -21,12 +21,14 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly FailRetryGraph graph;
private BeatmapInfo beatmap;
+
public BeatmapInfo Beatmap
{
- get { return beatmap; }
+ get => beatmap;
set
{
if (value == beatmap) return;
+
beatmap = value;
updateDisplay();
@@ -62,7 +64,7 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Success Rate",
- TextSize = 13,
+ Font = OsuFont.GetFont(size: 13)
},
successRate = new Bar
{
@@ -79,7 +81,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre,
- TextSize = 13,
+ Font = OsuFont.GetFont(size: 13),
},
},
graphLabel = new OsuSpriteText
@@ -87,7 +89,7 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Points of Failure",
- TextSize = 13,
+ Font = OsuFont.GetFont(size: 13),
Margin = new MarginPadding { Vertical = 20 },
},
},
diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs
index 27034ccccb..55c21b7fc9 100644
--- a/osu.Game/Overlays/BeatmapSetOverlay.cs
+++ b/osu.Game/Overlays/BeatmapSetOverlay.cs
@@ -3,6 +3,7 @@
using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -29,32 +30,20 @@ namespace osu.Game.Overlays
public const float RIGHT_WIDTH = 275;
private readonly Header header;
- private readonly Info info;
private APIAccess api;
private RulesetStore rulesets;
private readonly ScrollContainer scroll;
- private BeatmapSetInfo beatmapSet;
-
- public BeatmapSetInfo BeatmapSet
- {
- get => beatmapSet;
- set
- {
- if (value == beatmapSet)
- return;
-
- header.BeatmapSet = info.BeatmapSet = beatmapSet = value;
- }
- }
+ private readonly Bindable beatmapSet = new Bindable();
// receive input outside our bounds so we can trigger a close event on ourselves.
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public BeatmapSetOverlay()
{
+ Info info;
ScoresContainer scores;
Waves.FirstWaveColour = OsuColour.Gray(0.4f);
Waves.SecondWaveColour = OsuColour.Gray(0.3f);
@@ -101,10 +90,13 @@ namespace osu.Game.Overlays
},
};
+ header.BeatmapSet.BindTo(beatmapSet);
+ info.BeatmapSet.BindTo(beatmapSet);
+
header.Picker.Beatmap.ValueChanged += b =>
{
- info.Beatmap = b;
- scores.Beatmap = b;
+ info.Beatmap = b.NewValue;
+ scores.Beatmap = b.NewValue;
};
}
@@ -124,7 +116,7 @@ namespace osu.Game.Overlays
protected override void PopOut()
{
base.PopOut();
- FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null);
+ FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => beatmapSet.Value = null);
}
protected override bool OnClick(ClickEvent e)
@@ -135,12 +127,12 @@ namespace osu.Game.Overlays
public void FetchAndShowBeatmap(int beatmapId)
{
- BeatmapSet = null;
+ beatmapSet.Value = null;
var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId);
req.Success += res =>
{
- BeatmapSet = res.ToBeatmapSet(rulesets);
- header.Picker.Beatmap.Value = header.BeatmapSet.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
+ beatmapSet.Value = res.ToBeatmapSet(rulesets);
+ header.Picker.Beatmap.Value = header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
};
api.Queue(req);
Show();
@@ -148,16 +140,16 @@ namespace osu.Game.Overlays
public void FetchAndShowBeatmapSet(int beatmapSetId)
{
- BeatmapSet = null;
+ beatmapSet.Value = null;
var req = new GetBeatmapSetRequest(beatmapSetId);
- req.Success += res => BeatmapSet = res.ToBeatmapSet(rulesets);
+ req.Success += res => beatmapSet.Value = res.ToBeatmapSet(rulesets);
api.Queue(req);
Show();
}
public void ShowBeatmapSet(BeatmapSetInfo set)
{
- BeatmapSet = set;
+ beatmapSet.Value = set;
Show();
scroll.ScrollTo(0);
}
diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs
index 3e2ef07ef2..908ec5f026 100644
--- a/osu.Game/Overlays/Chat/ChatLine.cs
+++ b/osu.Game/Overlays/Chat/ChatLine.cs
@@ -87,9 +87,8 @@ namespace osu.Game.Overlays.Chat
Drawable effectedUsername = username = new OsuSpriteText
{
- Font = @"Exo2.0-BoldItalic",
Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
- TextSize = TextSize,
+ Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true)
};
if (hasBackground)
@@ -138,9 +137,7 @@ namespace osu.Game.Overlays.Chat
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
- Font = @"Exo2.0-SemiBold",
- FixedWidth = true,
- TextSize = TextSize * 0.75f,
+ Font = OsuFont.GetFont(size: TextSize * 0.75f, weight: FontWeight.SemiBold, fixedWidth: true)
},
new MessageSender(message.Sender)
{
@@ -162,13 +159,13 @@ namespace osu.Game.Overlays.Chat
{
if (Message.IsAction)
{
- t.Font = @"Exo2.0-MediumItalic";
+ t.Font = OsuFont.GetFont(italics: true);
if (senderHasBackground)
t.Colour = OsuColour.FromHex(message.Sender.Colour);
}
- t.TextSize = TextSize;
+ t.Font = t.Font.With(size: TextSize);
})
{
AutoSizeAxes = Axes.Y,
diff --git a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs
index 23dedf251f..a36abc4f99 100644
--- a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs
+++ b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
@@ -36,12 +36,10 @@ namespace osu.Game.Overlays.Chat.Selection
private Color4 hoverColour;
public IEnumerable FilterTerms => new[] { channel.Name };
+
public bool MatchingFilter
{
- set
- {
- this.FadeTo(value ? 1f : 0f, 100);
- }
+ set => this.FadeTo(value ? 1f : 0f, 100);
}
public Action OnRequestJoin;
@@ -54,7 +52,7 @@ namespace osu.Game.Overlays.Chat.Selection
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
- Action = () => { (channel.Joined ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); };
+ Action = () => { (channel.Joined.Value ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); };
Children = new Drawable[]
{
@@ -89,8 +87,7 @@ namespace osu.Game.Overlays.Chat.Selection
name = new OsuSpriteText
{
Text = channel.ToString(),
- TextSize = text_size,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold),
Shadow = false,
},
},
@@ -106,8 +103,7 @@ namespace osu.Game.Overlays.Chat.Selection
topic = new OsuSpriteText
{
Text = channel.Topic,
- TextSize = text_size,
- Font = @"Exo2.0-SemiBold",
+ Font = OsuFont.GetFont(size: text_size, weight: FontWeight.SemiBold),
Shadow = false,
},
},
@@ -130,8 +126,7 @@ namespace osu.Game.Overlays.Chat.Selection
new OsuSpriteText
{
Text = @"0",
- TextSize = text_size,
- Font = @"Exo2.0-SemiBold",
+ Font = OsuFont.GetFont(size: text_size, weight: FontWeight.SemiBold),
Shadow = false,
},
},
@@ -148,7 +143,7 @@ namespace osu.Game.Overlays.Chat.Selection
joinedColour = colours.Blue;
hoverColour = colours.Yellow;
- joinedBind.ValueChanged += updateColour;
+ joinedBind.ValueChanged += joined => updateColour(joined.NewValue);
joinedBind.BindTo(channel.Joined);
joinedBind.TriggerChange();
diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSection.cs b/osu.Game/Overlays/Chat/Selection/ChannelSection.cs
index 2af416f7c0..3f979b6309 100644
--- a/osu.Game/Overlays/Chat/Selection/ChannelSection.cs
+++ b/osu.Game/Overlays/Chat/Selection/ChannelSection.cs
@@ -7,6 +7,7 @@ using System.Linq;
using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
@@ -20,23 +21,21 @@ namespace osu.Game.Overlays.Chat.Selection
public IEnumerable FilterableChildren => ChannelFlow.Children;
public IEnumerable FilterTerms => Array.Empty();
+
public bool MatchingFilter
{
- set
- {
- this.FadeTo(value ? 1f : 0f, 100);
- }
+ set => this.FadeTo(value ? 1f : 0f, 100);
}
public string Header
{
- get { return header.Text; }
- set { header.Text = value.ToUpperInvariant(); }
+ get => header.Text;
+ set => header.Text = value.ToUpperInvariant();
}
public IEnumerable Channels
{
- set { ChannelFlow.ChildrenEnumerable = value.Select(c => new ChannelListItem(c)); }
+ set => ChannelFlow.ChildrenEnumerable = value.Select(c => new ChannelListItem(c));
}
public ChannelSection()
@@ -48,8 +47,7 @@ namespace osu.Game.Overlays.Chat.Selection
{
header = new OsuSpriteText
{
- TextSize = 15,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold),
},
ChannelFlow = new FillFlowContainer
{
diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs
index 00de5fd5fd..feb47b9e8e 100644
--- a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs
+++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs
@@ -110,7 +110,7 @@ namespace osu.Game.Overlays.Chat.Selection
new OsuSpriteText
{
Text = @"Chat Channels",
- TextSize = 20,
+ Font = OsuFont.GetFont(size: 20),
Shadow = false,
},
search = new HeaderSearchTextBox
@@ -125,7 +125,7 @@ namespace osu.Game.Overlays.Chat.Selection
},
};
- search.Current.ValueChanged += newValue => sectionsFlow.SearchTerm = newValue;
+ search.Current.ValueChanged += term => sectionsFlow.SearchTerm = term.NewValue;
}
public void UpdateAvailableChannels(IEnumerable channels)
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs
index d7d299a2cf..52260506fe 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs
@@ -13,15 +13,16 @@ namespace osu.Game.Overlays.Chat.Tabs
public override bool IsSwitchable => false;
- public ChannelSelectorTabItem(Channel value) : base(value)
+ public ChannelSelectorTabItem(Channel value)
+ : base(value)
{
Depth = float.MaxValue;
Width = 45;
Icon.Alpha = 0;
- Text.TextSize = 45;
- TextBold.TextSize = 45;
+ Text.Font = Text.Font.With(size: 45);
+ TextBold.Font = Text.Font.With(size: 45);
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
index ccf4af651b..7a43ca4b8c 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
@@ -7,9 +7,9 @@ using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat;
using osuTK;
-using osu.Framework.Configuration;
using System;
using System.Linq;
+using osu.Framework.Bindables;
namespace osu.Game.Overlays.Chat.Tabs
{
@@ -110,7 +110,7 @@ namespace osu.Game.Overlays.Chat.Tabs
if (tab == SelectedTab && totalTabs > 1)
// Select the tab after tab-to-be-removed's index, or the tab before if current == last
SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]);
- else if (totalTabs == 1 && !selectorTab.Active)
+ else if (totalTabs == 1 && !selectorTab.Active.Value)
// Open channel selection overlay if all channel tabs will be closed after removing this tab
SelectTab(selectorTab);
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs
index 7acd56c864..95c5fbf8fa 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs
@@ -90,7 +90,7 @@ namespace osu.Game.Overlays.Chat.Tabs
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = value.ToString(),
- TextSize = 18,
+ Font = OsuFont.GetFont(size: 18)
},
TextBold = new OsuSpriteText
{
@@ -99,8 +99,7 @@ namespace osu.Game.Overlays.Chat.Tabs
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = value.ToString(),
- Font = @"Exo2.0-Bold",
- TextSize = 18,
+ Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold)
},
CloseButton = new TabCloseButton
{
@@ -127,7 +126,7 @@ namespace osu.Game.Overlays.Chat.Tabs
if (IsRemovable && ShowCloseOnHover)
CloseButton.FadeIn(200, Easing.OutQuint);
- if (!Active)
+ if (!Active.Value)
box.FadeColour(backgroundHover, TRANSITION_LENGTH, Easing.OutQuint);
return true;
}
@@ -158,7 +157,7 @@ namespace osu.Game.Overlays.Chat.Tabs
private void updateState()
{
- if (Active)
+ if (Active.Value)
FadeActive();
else
FadeInactive();
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index 821c942a57..5428279325 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -6,7 +6,7 @@ using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -162,8 +162,8 @@ namespace osu.Game.Overlays
},
};
- channelTabControl.Current.ValueChanged += chat => channelManager.CurrentChannel.Value = chat;
- channelTabControl.ChannelSelectorActive.ValueChanged += value => channelSelectionOverlay.State = value ? Visibility.Visible : Visibility.Hidden;
+ channelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue;
+ channelTabControl.ChannelSelectorActive.ValueChanged += active => channelSelectionOverlay.State = active.NewValue ? Visibility.Visible : Visibility.Hidden;
channelSelectionOverlay.StateChanged += state =>
{
if (state == Visibility.Hidden && channelManager.CurrentChannel.Value == null)
@@ -189,9 +189,9 @@ namespace osu.Game.Overlays
channelSelectionOverlay.OnRequestLeave = channel => channelManager.LeaveChannel(channel);
}
- private void currentChannelChanged(Channel channel)
+ private void currentChannelChanged(ValueChangedEvent e)
{
- if (channel == null)
+ if (e.NewValue == null)
{
textbox.Current.Disabled = true;
currentChannelContainer.Clear(false);
@@ -199,18 +199,18 @@ namespace osu.Game.Overlays
return;
}
- textbox.Current.Disabled = channel.ReadOnly;
+ textbox.Current.Disabled = e.NewValue.ReadOnly;
- if (channelTabControl.Current.Value != channel)
- Scheduler.Add(() => channelTabControl.Current.Value = channel);
+ if (channelTabControl.Current.Value != e.NewValue)
+ Scheduler.Add(() => channelTabControl.Current.Value = e.NewValue);
- var loaded = loadedChannels.Find(d => d.Channel == channel);
+ var loaded = loadedChannels.Find(d => d.Channel == e.NewValue);
if (loaded == null)
{
currentChannelContainer.FadeOut(500, Easing.OutQuint);
loading.Show();
- loaded = new DrawableChannel(channel);
+ loaded = new DrawableChannel(e.NewValue);
loadedChannels.Add(loaded);
LoadComponentAsync(loaded, l =>
{
@@ -328,11 +328,11 @@ namespace osu.Game.Overlays
private void load(OsuConfigManager config, OsuColour colours, ChannelManager channelManager)
{
ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight);
- ChatHeight.ValueChanged += h =>
+ ChatHeight.ValueChanged += height =>
{
- chatContainer.Height = (float)h;
- channelSelectionContainer.Height = 1f - (float)h;
- tabBackground.FadeTo(h == 1 ? 1 : 0.8f, 200);
+ chatContainer.Height = (float)height.NewValue;
+ channelSelectionContainer.Height = 1f - (float)height.NewValue;
+ tabBackground.FadeTo(height.NewValue == 1 ? 1 : 0.8f, 200);
};
ChatHeight.TriggerChange();
diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs
index bf98d6ec49..72e3cc4f6a 100644
--- a/osu.Game/Overlays/Dialog/PopupDialog.cs
+++ b/osu.Game/Overlays/Dialog/PopupDialog.cs
@@ -53,6 +53,7 @@ namespace osu.Game.Overlays.Dialog
{
if (text == value)
return;
+
text = value;
header.Text = value;
@@ -171,7 +172,7 @@ namespace osu.Game.Overlays.Dialog
},
},
},
- header = new OsuTextFlowContainer(t => t.TextSize = 25)
+ header = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 25))
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
@@ -180,7 +181,7 @@ namespace osu.Game.Overlays.Dialog
Padding = new MarginPadding(15),
TextAnchor = Anchor.TopCentre,
},
- body = new OsuTextFlowContainer(t => t.TextSize = 18)
+ body = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 18))
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs
index 4ee6ff9698..1413f0f885 100644
--- a/osu.Game/Overlays/Direct/DirectGridPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs
@@ -29,7 +29,8 @@ namespace osu.Game.Overlays.Direct
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
- public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
+ public DirectGridPanel(BeatmapSetInfo beatmap)
+ : base(beatmap)
{
Width = 380;
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
@@ -75,13 +76,12 @@ namespace osu.Game.Overlays.Direct
new OsuSpriteText
{
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
- TextSize = 18,
- Font = @"Exo2.0-BoldItalic",
+ Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
},
new OsuSpriteText
{
Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
- Font = @"Exo2.0-BoldItalic",
+ Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
},
},
},
@@ -127,15 +127,14 @@ namespace osu.Game.Overlays.Direct
new OsuSpriteText
{
Text = "mapped by ",
- TextSize = 14,
+ Font = OsuFont.GetFont(size: 14),
Shadow = false,
Colour = colours.Gray5,
},
new OsuSpriteText
{
Text = SetInfo.Metadata.Author.Username,
- TextSize = 14,
- Font = @"Exo2.0-SemiBoldItalic",
+ Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true),
Shadow = false,
Colour = colours.BlueDark,
},
@@ -150,7 +149,7 @@ namespace osu.Game.Overlays.Direct
new OsuSpriteText
{
Text = SetInfo.Metadata.Source,
- TextSize = 14,
+ Font = OsuFont.GetFont(size: 14),
Shadow = false,
Colour = colours.Gray5,
Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f,
@@ -239,6 +238,6 @@ namespace osu.Game.Overlays.Direct
updateStatusContainer();
}
- private void updateStatusContainer() => statusContainer.FadeTo(IsHovered || PreviewPlaying ? 0 : 1, 120, Easing.InOutQuint);
+ private void updateStatusContainer() => statusContainer.FadeTo(IsHovered || PreviewPlaying.Value ? 0 : 1, 120, Easing.InOutQuint);
}
}
diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs
index cb74ae54a6..01393ad98b 100644
--- a/osu.Game/Overlays/Direct/DirectListPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectListPanel.cs
@@ -13,6 +13,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
namespace osu.Game.Overlays.Direct
{
@@ -23,6 +24,7 @@ namespace osu.Game.Overlays.Direct
private const float vertical_padding = 5;
private const float height = 70;
+ private FillFlowContainer statusContainer;
private PlayButton playButton;
private Box progressBar;
@@ -95,13 +97,12 @@ namespace osu.Game.Overlays.Direct
new OsuSpriteText
{
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
- TextSize = 18,
- Font = @"Exo2.0-BoldItalic",
+ Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
},
new OsuSpriteText
{
Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
- Font = @"Exo2.0-BoldItalic",
+ Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
},
}
},
@@ -109,10 +110,24 @@ namespace osu.Game.Overlays.Direct
},
new FillFlowContainer
{
- AutoSizeAxes = Axes.X,
- Height = 20,
- Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
- Children = GetDifficultyIcons(),
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ statusContainer = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Vertical = vertical_padding, Horizontal = 5 },
+ Spacing = new Vector2(5),
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.X,
+ Height = 20,
+ Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
+ Children = GetDifficultyIcons(),
+ },
+ },
},
},
},
@@ -161,13 +176,12 @@ namespace osu.Game.Overlays.Direct
new OsuSpriteText
{
Text = "mapped by ",
- TextSize = 14,
+ Font = OsuFont.GetFont(size: 14)
},
new OsuSpriteText
{
Text = SetInfo.Metadata.Author.Username,
- TextSize = 14,
- Font = @"Exo2.0-SemiBoldItalic",
+ Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true)
},
},
},
@@ -176,7 +190,7 @@ namespace osu.Game.Overlays.Direct
Text = SetInfo.Metadata.Source,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
- TextSize = 14,
+ Font = OsuFont.GetFont(size: 14),
Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f,
},
},
@@ -196,6 +210,23 @@ namespace osu.Game.Overlays.Direct
Colour = colours.Yellow,
},
});
+
+ if (SetInfo.OnlineInfo?.HasVideo ?? false)
+ {
+ statusContainer.Add(new IconPill(FontAwesome.fa_film) { IconSize = new Vector2(20) });
+ }
+
+ if (SetInfo.OnlineInfo?.HasStoryboard ?? false)
+ {
+ statusContainer.Add(new IconPill(FontAwesome.fa_image) { IconSize = new Vector2(20) });
+ }
+
+ statusContainer.Add(new BeatmapSetOnlineStatusPill
+ {
+ TextSize = 12,
+ TextPadding = new MarginPadding { Horizontal = 10, Vertical = 4 },
+ Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None,
+ });
}
}
}
diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs
index 22d7a1c7b5..3867886f6d 100644
--- a/osu.Game/Overlays/Direct/DirectPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectPanel.cs
@@ -4,7 +4,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -16,8 +16,6 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
-using osu.Game.Graphics.UserInterface;
-using osu.Game.Online.API.Requests;
using osuTK;
using osuTK.Graphics;
@@ -31,8 +29,6 @@ namespace osu.Game.Overlays.Direct
private Container content;
- private ProgressBar progressBar;
- private BeatmapManager beatmaps;
private BeatmapSetOverlay beatmapSetOverlay;
public PreviewTrack Preview => PlayButton.Preview;
@@ -65,14 +61,10 @@ namespace osu.Game.Overlays.Direct
Colour = Color4.Black.Opacity(0.3f),
};
- private OsuColour colours;
-
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{
- this.beatmaps = beatmaps;
this.beatmapSetOverlay = beatmapSetOverlay;
- this.colours = colours;
AddInternal(content = new Container
{
@@ -82,40 +74,21 @@ namespace osu.Game.Overlays.Direct
Children = new[]
{
CreateBackground(),
- progressBar = new ProgressBar
+ new DownloadProgressBar(SetInfo)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
- Height = 0,
- Alpha = 0,
- BackgroundColour = Color4.Black.Opacity(0.7f),
- FillColour = colours.Blue,
Depth = -1,
},
}
});
-
- var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
-
- if (downloadRequest != null)
- attachDownload(downloadRequest);
-
- beatmaps.BeatmapDownloadBegan += attachDownload;
- beatmaps.ItemAdded += setAdded;
- }
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
- beatmaps.BeatmapDownloadBegan -= attachDownload;
- beatmaps.ItemAdded -= setAdded;
}
protected override void Update()
{
base.Update();
- if (PreviewPlaying && Preview != null && Preview.TrackLoaded)
+ if (PreviewPlaying.Value && Preview != null && Preview.TrackLoaded)
{
PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length);
}
@@ -135,7 +108,7 @@ namespace osu.Game.Overlays.Direct
{
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
- if (FadePlayButton && !PreviewPlaying)
+ if (FadePlayButton && !PreviewPlaying.Value)
PlayButton.FadeOut(120, Easing.InOutQuint);
base.OnHoverLost(e);
@@ -149,44 +122,16 @@ namespace osu.Game.Overlays.Direct
protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
- private void attachDownload(DownloadBeatmapSetRequest request)
- {
- if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID)
- return;
-
- progressBar.FadeIn(400, Easing.OutQuint);
- progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
-
- progressBar.Current.Value = 0;
-
- request.Failure += e =>
- {
- progressBar.Current.Value = 0;
- progressBar.FadeOut(500);
- };
-
- request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress);
-
- request.Success += data =>
- {
- progressBar.Current.Value = 1;
- progressBar.FillColour = colours.Yellow;
- };
- }
-
- private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => Schedule(() =>
- {
- if (s.OnlineBeatmapSetID == SetInfo.OnlineBeatmapSetID)
- progressBar.FadeOut(500);
- });
-
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(200, Easing.Out);
- PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered || !FadePlayButton ? 1 : 0, 120, Easing.InOutQuint);
- PreviewPlaying.ValueChanged += newValue => PreviewBar.FadeTo(newValue ? 1 : 0, 120, Easing.InOutQuint);
+ PreviewPlaying.ValueChanged += playing =>
+ {
+ PlayButton.FadeTo(playing.NewValue || IsHovered || !FadePlayButton ? 1 : 0, 120, Easing.InOutQuint);
+ PreviewBar.FadeTo(playing.NewValue ? 1 : 0, 120, Easing.InOutQuint);
+ };
}
protected List GetDifficultyIcons()
@@ -213,7 +158,7 @@ namespace osu.Game.Overlays.Direct
public int Value
{
- get { return value; }
+ get => value;
set
{
this.value = value;
@@ -231,10 +176,7 @@ namespace osu.Game.Overlays.Direct
Children = new Drawable[]
{
- text = new OsuSpriteText
- {
- Font = @"Exo2.0-SemiBoldItalic",
- },
+ text = new OsuSpriteText { Font = OsuFont.GetFont(weight: FontWeight.SemiBold, italics: true) },
new SpriteIcon
{
Icon = icon,
diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs
index 1fa5035c81..f15413d522 100644
--- a/osu.Game/Overlays/Direct/DownloadButton.cs
+++ b/osu.Game/Overlays/Direct/DownloadButton.cs
@@ -5,103 +5,114 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
-using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osuTK;
namespace osu.Game.Overlays.Direct
{
- public class DownloadButton : OsuAnimatedButton
+ public class DownloadButton : DownloadTrackingComposite
{
- private readonly BeatmapSetInfo beatmapSet;
+ private readonly bool noVideo;
private readonly SpriteIcon icon;
private readonly SpriteIcon checkmark;
- private readonly BeatmapSetDownloader downloader;
private readonly Box background;
private OsuColour colours;
- public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
- {
- this.beatmapSet = beatmapSet;
+ private readonly ShakeContainer shakeContainer;
- AddRange(new Drawable[]
+ private readonly OsuAnimatedButton button;
+
+ public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
+ : base(beatmapSet)
+ {
+ this.noVideo = noVideo;
+
+ InternalChild = shakeContainer = new ShakeContainer
{
- downloader = new BeatmapSetDownloader(beatmapSet, noVideo),
- background = new Box
+ RelativeSizeAxes = Axes.Both,
+ Child = button = new OsuAnimatedButton
{
RelativeSizeAxes = Axes.Both,
- Depth = float.MaxValue
- },
- icon = new SpriteIcon
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Size = new Vector2(13),
- Icon = FontAwesome.fa_download,
- },
- checkmark = new SpriteIcon
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- X = 8,
- Size = Vector2.Zero,
- Icon = FontAwesome.fa_check,
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Depth = float.MaxValue
+ },
+ icon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(13),
+ Icon = FontAwesome.fa_download,
+ },
+ checkmark = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ X = 8,
+ Size = Vector2.Zero,
+ Icon = FontAwesome.fa_check,
+ }
+ }
}
- });
+ };
}
protected override void LoadComplete()
{
base.LoadComplete();
- downloader.DownloadState.BindValueChanged(updateState, true);
+
+ State.BindValueChanged(state => updateState(state.NewValue), true);
FinishTransforms(true);
}
[BackgroundDependencyLoader(permitNulls: true)]
- private void load(OsuColour colours, OsuGame game)
+ private void load(OsuColour colours, OsuGame game, BeatmapManager beatmaps)
{
this.colours = colours;
- Action = () =>
+ button.Action = () =>
{
- switch (downloader.DownloadState.Value)
+ switch (State.Value)
{
- case BeatmapSetDownloader.DownloadStatus.Downloading:
- // todo: replace with ShakeContainer after https://github.com/ppy/osu/pull/2909 is merged.
- Content.MoveToX(-5, 50, Easing.OutSine).Then()
- .MoveToX(5, 100, Easing.InOutSine).Then()
- .MoveToX(-5, 100, Easing.InOutSine).Then()
- .MoveToX(0, 50, Easing.InSine);
+ case DownloadState.Downloading:
+ case DownloadState.Downloaded:
+ shakeContainer.Shake();
break;
- case BeatmapSetDownloader.DownloadStatus.Downloaded:
- game.PresentBeatmap(beatmapSet);
+ case DownloadState.LocallyAvailable:
+ game.PresentBeatmap(BeatmapSet.Value);
break;
default:
- downloader.Download();
+ beatmaps.Download(BeatmapSet.Value, noVideo);
break;
}
};
}
- private void updateState(BeatmapSetDownloader.DownloadStatus state)
+ private void updateState(DownloadState state)
{
switch (state)
{
- case BeatmapSetDownloader.DownloadStatus.NotDownloaded:
+ case DownloadState.NotDownloaded:
background.FadeColour(colours.Gray4, 500, Easing.InOutExpo);
icon.MoveToX(0, 500, Easing.InOutExpo);
checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo);
break;
- case BeatmapSetDownloader.DownloadStatus.Downloading:
+ case DownloadState.Downloading:
background.FadeColour(colours.Blue, 500, Easing.InOutExpo);
icon.MoveToX(0, 500, Easing.InOutExpo);
checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo);
break;
-
- case BeatmapSetDownloader.DownloadStatus.Downloaded:
+ case DownloadState.Downloaded:
+ background.FadeColour(colours.Yellow, 500, Easing.InOutExpo);
+ break;
+ case DownloadState.LocallyAvailable:
background.FadeColour(colours.Green, 500, Easing.InOutExpo);
icon.MoveToX(-8, 500, Easing.InOutExpo);
checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo);
diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/Direct/DownloadProgressBar.cs
new file mode 100644
index 0000000000..9c2b1e5b63
--- /dev/null
+++ b/osu.Game/Overlays/Direct/DownloadProgressBar.cs
@@ -0,0 +1,64 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Graphics.UserInterface;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Direct
+{
+ public class DownloadProgressBar : DownloadTrackingComposite
+ {
+ private readonly ProgressBar progressBar;
+
+ public DownloadProgressBar(BeatmapSetInfo beatmapSet)
+ : base(beatmapSet)
+ {
+ AddInternal(progressBar = new ProgressBar
+ {
+ Height = 0,
+ Alpha = 0,
+ });
+
+ AutoSizeAxes = Axes.Y;
+ RelativeSizeAxes = Axes.X;
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuColour colours)
+ {
+ progressBar.FillColour = colours.Blue;
+ progressBar.BackgroundColour = Color4.Black.Opacity(0.7f);
+ progressBar.Current = Progress;
+
+ State.BindValueChanged(state =>
+ {
+ switch (state.NewValue)
+ {
+ case DownloadState.NotDownloaded:
+ progressBar.Current.Value = 0;
+ progressBar.FadeOut(500);
+ break;
+ case DownloadState.Downloading:
+ progressBar.FadeIn(400, Easing.OutQuint);
+ progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
+ break;
+ case DownloadState.Downloaded:
+ progressBar.FadeIn(400, Easing.OutQuint);
+ progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
+
+ progressBar.Current.Value = 1;
+ progressBar.FillColour = colours.Yellow;
+ break;
+ case DownloadState.LocallyAvailable:
+ progressBar.FadeOut(500);
+ break;
+ }
+ }, true);
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Direct/DownloadState.cs b/osu.Game/Overlays/Direct/DownloadState.cs
new file mode 100644
index 0000000000..a301c6a3bd
--- /dev/null
+++ b/osu.Game/Overlays/Direct/DownloadState.cs
@@ -0,0 +1,13 @@
+// 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.Overlays.Direct
+{
+ public enum DownloadState
+ {
+ NotDownloaded,
+ Downloading,
+ Downloaded,
+ LocallyAvailable
+ }
+}
diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs
new file mode 100644
index 0000000000..37f13aefc8
--- /dev/null
+++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs
@@ -0,0 +1,133 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Online.API.Requests;
+
+namespace osu.Game.Overlays.Direct
+{
+ ///
+ /// A component which tracks a beatmap through potential download/import/deletion.
+ ///
+ public abstract class DownloadTrackingComposite : CompositeDrawable
+ {
+ public readonly Bindable BeatmapSet = new Bindable();
+
+ private BeatmapManager beatmaps;
+
+ ///
+ /// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
+ ///
+ protected readonly Bindable State = new Bindable();
+
+ protected readonly Bindable Progress = new Bindable();
+
+ protected DownloadTrackingComposite(BeatmapSetInfo beatmapSet = null)
+ {
+ BeatmapSet.Value = beatmapSet;
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load(BeatmapManager beatmaps)
+ {
+ this.beatmaps = beatmaps;
+
+ BeatmapSet.BindValueChanged(setInfo =>
+ {
+ if (setInfo.NewValue == null)
+ attachDownload(null);
+ else if (beatmaps.GetAllUsableBeatmapSetsEnumerable().Any(s => s.OnlineBeatmapSetID == setInfo.NewValue.OnlineBeatmapSetID))
+ State.Value = DownloadState.LocallyAvailable;
+ else
+ attachDownload(beatmaps.GetExistingDownload(setInfo.NewValue));
+ }, true);
+
+ beatmaps.BeatmapDownloadBegan += download =>
+ {
+ if (download.BeatmapSet.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID)
+ attachDownload(download);
+ };
+
+ beatmaps.ItemAdded += setAdded;
+ beatmaps.ItemRemoved += setRemoved;
+ }
+
+ #region Disposal
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ if (beatmaps != null)
+ {
+ beatmaps.BeatmapDownloadBegan -= attachDownload;
+ beatmaps.ItemAdded -= setAdded;
+ }
+
+ State.UnbindAll();
+
+ attachDownload(null);
+ }
+
+ #endregion
+
+ private DownloadBeatmapSetRequest attachedRequest;
+
+ private void attachDownload(DownloadBeatmapSetRequest request)
+ {
+ if (attachedRequest != null)
+ {
+ attachedRequest.Failure -= onRequestFailure;
+ attachedRequest.DownloadProgressed -= onRequestProgress;
+ attachedRequest.Success -= onRequestSuccess;
+ }
+
+ attachedRequest = request;
+
+ if (attachedRequest != null)
+ {
+ if (attachedRequest.Progress == 1)
+ {
+ State.Value = DownloadState.Downloaded;
+ Progress.Value = 1;
+ }
+ else
+ {
+ State.Value = DownloadState.Downloading;
+ Progress.Value = attachedRequest.Progress;
+
+ attachedRequest.Failure += onRequestFailure;
+ attachedRequest.DownloadProgressed += onRequestProgress;
+ attachedRequest.Success += onRequestSuccess;
+ }
+ }
+ else
+ {
+ State.Value = DownloadState.NotDownloaded;
+ }
+ }
+
+ private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Downloaded);
+
+ private void onRequestProgress(float progress) => Schedule(() => Progress.Value = progress);
+
+ private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null));
+
+ private void setAdded(BeatmapSetInfo s, bool existing) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable);
+
+ private void setRemoved(BeatmapSetInfo s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded);
+
+ private void setDownloadStateFromManager(BeatmapSetInfo s, DownloadState state) => Schedule(() =>
+ {
+ if (s.OnlineBeatmapSetID != BeatmapSet.Value?.OnlineBeatmapSetID)
+ return;
+
+ State.Value = state;
+ });
+ }
+}
diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs
index f1ae435c73..d7e0760fc6 100644
--- a/osu.Game/Overlays/Direct/FilterControl.cs
+++ b/osu.Game/Overlays/Direct/FilterControl.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
@@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Direct
{
DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark;
- Ruleset.Value = ruleset ?? rulesets.GetRuleset(0);
+ Ruleset.Value = ruleset.Value ?? rulesets.GetRuleset(0);
foreach (var r in rulesets.AvailableRulesets)
modeButtons.Add(new RulesetToggleButton(Ruleset, r));
}
@@ -68,9 +68,9 @@ namespace osu.Game.Overlays.Direct
private readonly ConstrainedIconContainer iconContainer;
- private void Bindable_ValueChanged(RulesetInfo obj)
+ private void Bindable_ValueChanged(ValueChangedEvent e)
{
- iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100);
+ iconContainer.FadeTo(Ruleset.ID == e.NewValue?.ID ? 1f : 0.5f, 100);
}
public override bool HandleNonPositionalInput => !bindable.Disabled && base.HandleNonPositionalInput;
@@ -93,7 +93,7 @@ namespace osu.Game.Overlays.Direct
Ruleset = ruleset;
bindable.ValueChanged += Bindable_ValueChanged;
- Bindable_ValueChanged(bindable.Value);
+ Bindable_ValueChanged(new ValueChangedEvent(bindable.Value, bindable.Value));
Action = () => bindable.Value = Ruleset;
}
diff --git a/osu.Game/Overlays/Direct/Header.cs b/osu.Game/Overlays/Direct/Header.cs
index 267590f19a..e85cb3b4ac 100644
--- a/osu.Game/Overlays/Direct/Header.cs
+++ b/osu.Game/Overlays/Direct/Header.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Direct
protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a");
protected override DirectTab DefaultTab => DirectTab.Search;
- protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 };
+ protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", Font = OsuFont.GetFont(size: 25) };
protected override FontAwesome Icon => FontAwesome.fa_osu_chevron_down_o;
public Header()
@@ -28,10 +28,13 @@ namespace osu.Game.Overlays.Direct
public enum DirectTab
{
Search,
+
[Description("Newest Maps")]
NewestMaps = DirectSortCriteria.Ranked,
+
[Description("Top Rated")]
TopRated = DirectSortCriteria.Rating,
+
[Description("Most Played")]
MostPlayed = DirectSortCriteria.Plays,
}
diff --git a/osu.Game/Overlays/Direct/IconPill.cs b/osu.Game/Overlays/Direct/IconPill.cs
index 83ee0cb6c5..e7f516f449 100644
--- a/osu.Game/Overlays/Direct/IconPill.cs
+++ b/osu.Game/Overlays/Direct/IconPill.cs
@@ -12,6 +12,14 @@ namespace osu.Game.Overlays.Direct
{
public class IconPill : CircularContainer
{
+ public Vector2 IconSize
+ {
+ get => iconContainer.Size;
+ set => iconContainer.Size = value;
+ }
+
+ private readonly Container iconContainer;
+
public IconPill(FontAwesome icon)
{
AutoSizeAxes = Axes.Both;
@@ -25,16 +33,16 @@ namespace osu.Game.Overlays.Direct
Colour = Color4.Black,
Alpha = 0.5f,
},
- new Container
+ iconContainer = new Container
{
- AutoSizeAxes = Axes.Both,
- Margin = new MarginPadding(5),
+ Size = new Vector2(22),
+ Padding = new MarginPadding(5),
Child = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
Icon = icon,
- Size = new Vector2(12),
},
},
};
diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs
index 5e3ad3344e..3c5508ba00 100644
--- a/osu.Game/Overlays/Direct/PlayButton.cs
+++ b/osu.Game/Overlays/Direct/PlayButton.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
@@ -24,10 +24,11 @@ namespace osu.Game.Overlays.Direct
public BeatmapSetInfo BeatmapSet
{
- get { return beatmapSet; }
+ get => beatmapSet;
set
{
if (value == beatmapSet) return;
+
beatmapSet = value;
Preview?.Stop();
@@ -112,12 +113,12 @@ namespace osu.Game.Overlays.Direct
base.OnHoverLost(e);
}
- private void playingStateChanged(bool playing)
+ private void playingStateChanged(ValueChangedEvent e)
{
- icon.Icon = playing ? FontAwesome.fa_stop : FontAwesome.fa_play;
- icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
+ icon.Icon = e.NewValue ? FontAwesome.fa_stop : FontAwesome.fa_play;
+ icon.FadeColour(e.NewValue || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
- if (playing)
+ if (e.NewValue)
{
if (BeatmapSet == null)
{
@@ -144,7 +145,7 @@ namespace osu.Game.Overlays.Direct
preview.Stopped += () => Playing.Value = false;
// user may have changed their mind.
- if (Playing)
+ if (Playing.Value)
preview.Start();
});
}
diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs
index 7dd59bf0bc..0dc74b6a88 100644
--- a/osu.Game/Overlays/DirectOverlay.cs
+++ b/osu.Game/Overlays/DirectOverlay.cs
@@ -4,8 +4,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Humanizer;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
@@ -45,7 +46,7 @@ namespace osu.Game.Overlays
public IEnumerable BeatmapSets
{
- get { return beatmapSets; }
+ get => beatmapSets;
set
{
if (beatmapSets?.Equals(value) ?? false) return;
@@ -72,10 +73,11 @@ namespace osu.Game.Overlays
public ResultCounts ResultAmounts
{
- get { return resultAmounts; }
+ get => resultAmounts;
set
{
if (value == ResultAmounts) return;
+
resultAmounts = value;
updateResultCounts();
@@ -105,12 +107,11 @@ namespace osu.Game.Overlays
new OsuSpriteText
{
Text = "Found ",
- TextSize = 15,
+ Font = OsuFont.GetFont(size: 15)
},
resultCountsText = new OsuSpriteText
{
- TextSize = 15,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold)
},
}
},
@@ -118,7 +119,7 @@ namespace osu.Game.Overlays
Filter.Search.Current.ValueChanged += text =>
{
- if (text != string.Empty)
+ if (text.NewValue != string.Empty)
{
Header.Tabs.Current.Value = DirectTab.Search;
@@ -133,13 +134,13 @@ namespace osu.Game.Overlays
Filter.Tabs.Current.Value = DirectSortCriteria.Ranked;
}
};
- ((FilterControl)Filter).Ruleset.ValueChanged += ruleset => Scheduler.AddOnce(updateSearch);
- Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels;
- Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += rankStatus => Scheduler.AddOnce(updateSearch);
+ ((FilterControl)Filter).Ruleset.ValueChanged += _ => Scheduler.AddOnce(updateSearch);
+ Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue);
+ Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch);
Header.Tabs.Current.ValueChanged += tab =>
{
- if (tab != DirectTab.Search)
+ if (tab.NewValue != DirectTab.Search)
{
currentQuery.Value = string.Empty;
Filter.Tabs.Current.Value = (DirectSortCriteria)Header.Tabs.Current.Value;
@@ -147,11 +148,11 @@ namespace osu.Game.Overlays
}
};
- currentQuery.ValueChanged += v =>
+ currentQuery.ValueChanged += text =>
{
queryChangedDebounce?.Cancel();
- if (string.IsNullOrEmpty(v))
+ if (string.IsNullOrEmpty(text.NewValue))
Scheduler.AddOnce(updateSearch);
else
{
@@ -164,9 +165,9 @@ namespace osu.Game.Overlays
currentQuery.BindTo(Filter.Search.Current);
- Filter.Tabs.Current.ValueChanged += sortCriteria =>
+ Filter.Tabs.Current.ValueChanged += tab =>
{
- if (Header.Tabs.Current.Value != DirectTab.Search && sortCriteria != (DirectSortCriteria)Header.Tabs.Current.Value)
+ if (Header.Tabs.Current.Value != DirectTab.Search && tab.NewValue != (DirectSortCriteria)Header.Tabs.Current.Value)
Header.Tabs.Current.Value = DirectTab.Search;
Scheduler.AddOnce(updateSearch);
@@ -190,14 +191,9 @@ namespace osu.Game.Overlays
resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, Easing.OutQuint);
if (ResultAmounts == null) return;
- resultCountsText.Text = pluralize("Artist", ResultAmounts.Artists) + ", " +
- pluralize("Song", ResultAmounts.Songs) + ", " +
- pluralize("Tag", ResultAmounts.Tags);
- }
-
- private string pluralize(string prefix, int value)
- {
- return $@"{value} {prefix}" + (value == 1 ? string.Empty : @"s");
+ resultCountsText.Text = "Artist".ToQuantity(ResultAmounts.Artists) + ", " +
+ "Song".ToQuantity(ResultAmounts.Songs) + ", " +
+ "Tag".ToQuantity(ResultAmounts.Tags);
}
private void recreatePanels(PanelDisplayStyle displayStyle)
@@ -274,9 +270,6 @@ namespace osu.Game.Overlays
if (api == null)
return;
- if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty))
- return;
-
previewTrackManager.StopAnyPlaying(this);
getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty,
diff --git a/osu.Game/Overlays/HoldToConfirmOverlay.cs b/osu.Game/Overlays/HoldToConfirmOverlay.cs
index 2854fdab50..154aff605a 100644
--- a/osu.Game/Overlays/HoldToConfirmOverlay.cs
+++ b/osu.Game/Overlays/HoldToConfirmOverlay.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Overlays
}
};
- Progress.ValueChanged += v => overlay.Alpha = (float)v;
+ Progress.ValueChanged += p => overlay.Alpha = (float)p.NewValue;
}
}
}
diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
index f5f2ec453b..82e24f550b 100644
--- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
+++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Overlays.KeyBinding
{
public class GlobalKeyBindingsSection : SettingsSection
{
- public override FontAwesome Icon => FontAwesome.fa_osu_hot;
+ public override FontAwesome Icon => FontAwesome.fa_globe;
public override string Header => "Global";
public GlobalKeyBindingsSection(GlobalActionContainer manager)
@@ -33,7 +33,8 @@ namespace osu.Game.Overlays.KeyBinding
{
protected override string Header => "In Game";
- public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null)
+ public InGameKeyBindingsSubsection(GlobalActionContainer manager)
+ : base(null)
{
Defaults = manager.InGameKeyBindings;
}
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
index 3dad94c8fa..ef16c81dfc 100644
--- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
+++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Overlays.KeyBinding
public bool MatchingFilter
{
- get { return matchingFilter; }
+ get => matchingFilter;
set
{
matchingFilter = value;
@@ -309,10 +309,11 @@ namespace osu.Game.Overlays.KeyBinding
public bool IsBinding
{
- get { return isBinding; }
+ get => isBinding;
set
{
if (value == isBinding) return;
+
isBinding = value;
updateHoverState();
@@ -349,8 +350,7 @@ namespace osu.Game.Overlays.KeyBinding
},
Text = new OsuSpriteText
{
- Font = "Venera",
- TextSize = 10,
+ Font = OsuFont.Numeric.With(size: 10),
Margin = new MarginPadding(5),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs
index cfd3aaf6f7..7b3bef90c0 100644
--- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs
+++ b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Overlays.KeyBinding
{
public class RulesetBindingsSection : SettingsSection
{
- public override FontAwesome Icon => FontAwesome.fa_osu_hot;
+ public override FontAwesome Icon => (ruleset.CreateInstance().CreateIcon() as SpriteIcon)?.Icon ?? FontAwesome.fa_osu_hot;
public override string Header => ruleset.Name;
private readonly RulesetInfo ruleset;
diff --git a/osu.Game/Overlays/KeyBindingOverlay.cs b/osu.Game/Overlays/KeyBindingOverlay.cs
index 38ac83f041..300563dc59 100644
--- a/osu.Game/Overlays/KeyBindingOverlay.cs
+++ b/osu.Game/Overlays/KeyBindingOverlay.cs
@@ -3,10 +3,17 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Input.Bindings;
+using osu.Framework.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
using osu.Game.Input.Bindings;
using osu.Game.Overlays.KeyBinding;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
+using osu.Game.Screens.Ranking;
+using osuTK;
namespace osu.Game.Overlays
{
@@ -21,11 +28,84 @@ namespace osu.Game.Overlays
foreach (var ruleset in rulesets.AvailableRulesets)
AddSection(new RulesetBindingsSection(ruleset));
+
+ AddInternal(new BackButton
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Action = Hide
+ });
}
public KeyBindingOverlay()
- : base(false)
+ : base(true)
{
}
+
+ private class BackButton : OsuClickableContainer, IKeyBindingHandler
+ {
+ private AspectContainer aspect;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Size = new Vector2(Sidebar.DEFAULT_WIDTH);
+ Children = new Drawable[]
+ {
+ aspect = new AspectContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Y = -15,
+ Size = new Vector2(15),
+ Shadow = true,
+ Icon = FontAwesome.fa_chevron_left
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Y = 15,
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
+ Text = @"back",
+ },
+ }
+ }
+ };
+ }
+
+ protected override bool OnMouseDown(MouseDownEvent e)
+ {
+ aspect.ScaleTo(0.75f, 2000, Easing.OutQuint);
+ return base.OnMouseDown(e);
+ }
+
+ protected override bool OnMouseUp(MouseUpEvent e)
+ {
+ aspect.ScaleTo(1, 1000, Easing.OutElastic);
+ return base.OnMouseUp(e);
+ }
+
+ public bool OnPressed(GlobalAction action)
+ {
+ switch (action)
+ {
+ case GlobalAction.Back:
+ Click();
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool OnReleased(GlobalAction action) => false;
+ }
}
}
diff --git a/osu.Game/Overlays/MainSettings.cs b/osu.Game/Overlays/MainSettings.cs
index 68fa651dfa..61dd51d16f 100644
--- a/osu.Game/Overlays/MainSettings.cs
+++ b/osu.Game/Overlays/MainSettings.cs
@@ -1,28 +1,19 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input.Bindings;
-using osu.Framework.Input.Events;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Containers;
-using osu.Game.Graphics.Sprites;
-using osu.Game.Input.Bindings;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections;
-using osu.Game.Screens.Ranking;
-using osuTK;
using osuTK.Graphics;
+using System.Collections.Generic;
namespace osu.Game.Overlays
{
public class MainSettings : SettingsOverlay
{
private readonly KeyBindingOverlay keyBindingOverlay;
- private BackButton backButton;
protected override IEnumerable CreateSections() => new SettingsSection[]
{
@@ -53,8 +44,6 @@ namespace osu.Game.Overlays
public override bool AcceptsFocus => keyBindingOverlay.State != Visibility.Visible;
- private const float hidden_width = 120;
-
private void keyBindingOverlay_StateChanged(Visibility visibility)
{
switch (visibility)
@@ -64,9 +53,7 @@ namespace osu.Game.Overlays
Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint);
SectionsContainer.FadeOut(300, Easing.OutQuint);
- ContentContainer.MoveToX(hidden_width - WIDTH, 500, Easing.OutQuint);
-
- backButton.Delay(100).FadeIn(100);
+ ContentContainer.MoveToX(-WIDTH, 500, Easing.OutQuint);
break;
case Visibility.Hidden:
Background.FadeTo(0.6f, 500, Easing.OutQuint);
@@ -74,94 +61,16 @@ namespace osu.Game.Overlays
SectionsContainer.FadeIn(500, Easing.OutQuint);
ContentContainer.MoveToX(0, 500, Easing.OutQuint);
-
- backButton.FadeOut(100);
break;
}
}
- protected override float ExpandedPosition => keyBindingOverlay.State == Visibility.Visible ? hidden_width - WIDTH : base.ExpandedPosition;
+ protected override float ExpandedPosition => keyBindingOverlay.State == Visibility.Visible ? -WIDTH : base.ExpandedPosition;
[BackgroundDependencyLoader]
private void load()
{
ContentContainer.Add(keyBindingOverlay);
-
- ContentContainer.Add(backButton = new BackButton
- {
- Alpha = 0,
- Width = hidden_width,
- RelativeSizeAxes = Axes.Y,
- Anchor = Anchor.CentreRight,
- Origin = Anchor.CentreRight,
- Action = () => keyBindingOverlay.Hide()
- });
- }
-
- private class BackButton : OsuClickableContainer, IKeyBindingHandler
- {
- private AspectContainer aspect;
-
- [BackgroundDependencyLoader]
- private void load()
- {
- Children = new Drawable[]
- {
- aspect = new AspectContainer
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Y,
- Children = new Drawable[]
- {
- new SpriteIcon
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Y = -15,
- Size = new Vector2(15),
- Shadow = true,
- Icon = FontAwesome.fa_chevron_left
- },
- new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Y = 15,
- TextSize = 12,
- Font = @"Exo2.0-Bold",
- Text = @"back",
- },
- }
- }
- };
- }
-
- protected override bool OnMouseDown(MouseDownEvent e)
- {
- aspect.ScaleTo(0.75f, 2000, Easing.OutQuint);
- return base.OnMouseDown(e);
- }
-
- protected override bool OnMouseUp(MouseUpEvent e)
- {
- aspect.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(e);
- }
-
- public bool OnPressed(GlobalAction action)
- {
- switch (action)
- {
- case GlobalAction.Back:
- Click();
- return true;
- }
-
- return false;
- }
-
- public bool OnReleased(GlobalAction action) => false;
}
}
}
diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs
index 76efd74006..a5703eba92 100644
--- a/osu.Game/Overlays/MedalOverlay.cs
+++ b/osu.Game/Overlays/MedalOverlay.cs
@@ -219,13 +219,11 @@ namespace osu.Game.Overlays
{
if (drawableMedal.State != DisplayState.Full)
drawableMedal.State = DisplayState.Icon;
- })
- .Delay(step_duration).Schedule(() =>
+ }).Delay(step_duration).Schedule(() =>
{
if (drawableMedal.State != DisplayState.Full)
drawableMedal.State = DisplayState.MedalUnlocked;
- })
- .Delay(step_duration).Schedule(() =>
+ }).Delay(step_duration).Schedule(() =>
{
if (drawableMedal.State != DisplayState.Full)
drawableMedal.State = DisplayState.Full;
diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
index a7579b4d4b..eeb42ec991 100644
--- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
+++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs
@@ -29,6 +29,7 @@ namespace osu.Game.Overlays.MedalSplash
private readonly OsuSpriteText unlocked, name;
private readonly TextFlowContainer description;
private DisplayState state;
+
public DrawableMedal(Medal medal)
{
this.medal = medal;
@@ -63,8 +64,7 @@ namespace osu.Game.Overlays.MedalSplash
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Medal Unlocked".ToUpperInvariant(),
- TextSize = 24,
- Font = @"Exo2.0-Light",
+ Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light),
Alpha = 0f,
Scale = new Vector2(1f / scale_when_unlocked),
},
@@ -84,8 +84,7 @@ namespace osu.Game.Overlays.MedalSplash
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = medal.Name,
- TextSize = 20,
- Font = @"Exo2.0-Bold",
+ Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold),
Alpha = 0f,
Scale = new Vector2(1f / scale_when_full),
},
@@ -107,7 +106,7 @@ namespace osu.Game.Overlays.MedalSplash
{
s.Anchor = Anchor.TopCentre;
s.Origin = Anchor.TopCentre;
- s.TextSize = 16;
+ s.Font = s.Font.With(size: 16);
});
medalContainer.OnLoadComplete = d =>
@@ -134,7 +133,7 @@ namespace osu.Game.Overlays.MedalSplash
public DisplayState State
{
- get { return state; }
+ get => state;
set
{
if (state == value) return;
diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs
index e1fb8674de..23b75caedc 100644
--- a/osu.Game/Overlays/Mods/ModButton.cs
+++ b/osu.Game/Overlays/Mods/ModButton.cs
@@ -14,6 +14,7 @@ using System;
using System.Linq;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input.Events;
+using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Mods
@@ -106,10 +107,11 @@ namespace osu.Game.Overlays.Mods
public Color4 SelectedColour
{
- get { return selectedColour; }
+ get => selectedColour;
set
{
if (value == selectedColour) return;
+
selectedColour = value;
if (Selected) foregroundIcon.Colour = value;
}
@@ -120,7 +122,7 @@ namespace osu.Game.Overlays.Mods
public Mod Mod
{
- get { return mod; }
+ get => mod;
set
{
mod = value;
@@ -275,7 +277,7 @@ namespace osu.Game.Overlays.Mods
Y = 75,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
- TextSize = 18,
+ Font = OsuFont.GetFont(size: 18)
},
new HoverClickSounds()
};
diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs
index 0530f812e3..a118357f21 100644
--- a/osu.Game/Overlays/Mods/ModSection.cs
+++ b/osu.Game/Overlays/Mods/ModSection.cs
@@ -11,6 +11,7 @@ using System;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.Input.Events;
+using osu.Game.Graphics;
namespace osu.Game.Overlays.Mods
{
@@ -80,6 +81,7 @@ namespace osu.Game.Overlays.Mods
{
Mod selected = button.SelectedMod;
if (selected == null) continue;
+
foreach (var type in modTypes)
if (type.IsInstanceOfType(selected))
{
@@ -123,7 +125,7 @@ namespace osu.Game.Overlays.Mods
Origin = Anchor.TopLeft,
Anchor = Anchor.TopLeft,
Position = new Vector2(0f, 0f),
- Font = @"Exo2.0-Bold"
+ Font = OsuFont.GetFont(weight: FontWeight.Bold)
},
ButtonsContainer = new FillFlowContainer
{
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index 386dd01ebd..aa41723ca6 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -4,7 +4,6 @@
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -17,6 +16,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets;
@@ -38,6 +38,8 @@ namespace osu.Game.Overlays.Mods
protected override bool BlockNonPositionalInput => false;
+ protected override bool DimMainContent => false;
+
protected readonly FillFlowContainer