mirror of
https://github.com/ppy/osu.git
synced 2025-02-15 20:05:29 +08:00
Merge branch 'master' into sheared_slider_implementation_clean_ii
This commit is contained in:
commit
c691a0d3af
6
Gemfile
6
Gemfile
@ -1,6 +0,0 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
|
||||
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
||||
eval_gemfile(plugins_path) if File.exist?(plugins_path)
|
234
Gemfile.lock
234
Gemfile.lock
@ -1,234 +0,0 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.5)
|
||||
rexml
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.653.0)
|
||||
aws-sdk-core (3.166.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.59.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.117.1)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.5.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.6.4)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.93.1)
|
||||
faraday (1.10.2)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.4)
|
||||
multipart-post (~> 2)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.6)
|
||||
fastlane (2.210.1)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander (~> 4.6)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (~> 2.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (~> 0.1.1)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.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.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-clean_testflight_testers (0.3.0)
|
||||
fastlane-plugin-souyuz (0.11.1)
|
||||
souyuz (= 0.11.1)
|
||||
fastlane-plugin-xamarin (0.6.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.29.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-apis-core (0.9.1)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.15.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.12.0)
|
||||
google-apis-core (>= 0.9.1, < 2.a)
|
||||
google-apis-storage_v1 (0.19.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.3.0)
|
||||
google-cloud-storage (1.43.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.19.0)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.3.0)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.5)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.6.1)
|
||||
json (2.6.2)
|
||||
jwt (2.5.0)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.8.0)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
nokogiri (1.13.9)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
optparse (0.1.1)
|
||||
os (1.1.4)
|
||||
plist (3.6.0)
|
||||
public_suffix (5.0.0)
|
||||
racc (1.6.0)
|
||||
rake (13.0.6)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
security (0.1.3)
|
||||
signet (0.17.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.8)
|
||||
CFPropertyList
|
||||
naturally
|
||||
souyuz (0.11.1)
|
||||
fastlane (>= 2.182.0)
|
||||
highline (~> 2.0)
|
||||
nokogiri (~> 1.7)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.7.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.22.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
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
|
@ -1,2 +0,0 @@
|
||||
app_identifier("sh.ppy.osulazer") # The bundle identifier of your app
|
||||
apple_id("apple-dev@ppy.sh") # Your Apple email address
|
@ -1,147 +0,0 @@
|
||||
update_fastlane
|
||||
|
||||
platform :android do
|
||||
desc 'Deploy to play store'
|
||||
lane :beta do |options|
|
||||
|
||||
update_version(
|
||||
version: options[:version],
|
||||
build: options[:build],
|
||||
)
|
||||
|
||||
build(options)
|
||||
|
||||
supply(
|
||||
apk: './osu.Android/bin/Release/sh.ppy.osulazer-Signed.apk',
|
||||
package_name: 'sh.ppy.osulazer',
|
||||
track: 'alpha', # upload to alpha, we can promote it later
|
||||
json_key: options[:json_key],
|
||||
)
|
||||
end
|
||||
|
||||
desc 'Deploy to github release'
|
||||
lane :build_github do |options|
|
||||
|
||||
update_version(
|
||||
version: options[:version],
|
||||
build: options[:build],
|
||||
)
|
||||
|
||||
build(options)
|
||||
|
||||
client = HTTPClient.new
|
||||
changelog = client.get_content 'https://gist.githubusercontent.com/peppy/aaa2ec1a323554b619671cac6dbbb776/raw'
|
||||
changelog.gsub!('$BUILD_ID', options[:build])
|
||||
|
||||
set_github_release(
|
||||
repository_name: "ppy/osu",
|
||||
api_token: ENV["GITHUB_TOKEN"],
|
||||
name: options[:build],
|
||||
tag_name: options[:build],
|
||||
is_draft: true,
|
||||
description: changelog,
|
||||
commitish: "master",
|
||||
upload_assets: ["osu.Android/bin/Release/sh.ppy.osulazer.apk"]
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
desc 'Compile the project'
|
||||
lane :build do |options|
|
||||
nuget_restore(project_path: 'osu.Android/osu.Android.csproj')
|
||||
nuget_restore(project_path: 'osu.Game/osu.Game.csproj')
|
||||
nuget_restore(project_path: 'osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj')
|
||||
nuget_restore(project_path: 'osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj')
|
||||
nuget_restore(project_path: 'osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj')
|
||||
nuget_restore(project_path: 'osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj')
|
||||
|
||||
souyuz(
|
||||
build_configuration: 'Release',
|
||||
solution_path: 'osu.sln',
|
||||
platform: "android",
|
||||
output_path: "osu.Android/bin/Release/",
|
||||
keystore_path: options[:keystore_path],
|
||||
keystore_alias: options[:keystore_alias],
|
||||
keystore_password: ENV["KEYSTORE_PASSWORD"]
|
||||
)
|
||||
end
|
||||
|
||||
lane :update_version do |options|
|
||||
|
||||
split = options[:build].split('.')
|
||||
split[1] = split[1].to_s.rjust(4, '0')
|
||||
android_build = split.join('')
|
||||
|
||||
app_version(
|
||||
solution_path: 'osu.sln',
|
||||
version: options[:version],
|
||||
build: android_build,
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
platform :ios do
|
||||
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,
|
||||
groups: ['osu! supporters', 'public'],
|
||||
distribute_external: true,
|
||||
ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa'
|
||||
)
|
||||
end
|
||||
|
||||
desc 'Compile the project'
|
||||
lane :build do
|
||||
nuget_restore(project_path: 'osu.iOS/osu.iOS.csproj')
|
||||
nuget_restore(project_path: 'osu.Game/osu.Game.csproj')
|
||||
nuget_restore(project_path: 'osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj')
|
||||
nuget_restore(project_path: 'osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj')
|
||||
nuget_restore(project_path: 'osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj')
|
||||
nuget_restore(project_path: 'osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj')
|
||||
|
||||
souyuz(
|
||||
platform: "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
|
||||
|
||||
lane :update_version do |options|
|
||||
options[:plist_path] = 'osu.iOS/Info.plist'
|
||||
app_version(options)
|
||||
end
|
||||
|
||||
lane :testflight_prune_dry do
|
||||
clean_testflight_testers(days_of_inactivity:30, dry_run: true)
|
||||
end
|
||||
|
||||
lane :testflight_prune do
|
||||
clean_testflight_testers(days_of_inactivity: 30)
|
||||
end
|
||||
end
|
@ -1 +0,0 @@
|
||||
git_url('https://github.com/peppy/apple-certificates')
|
@ -1,7 +0,0 @@
|
||||
# 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'
|
@ -1,109 +0,0 @@
|
||||
fastlane documentation
|
||||
----
|
||||
|
||||
# Installation
|
||||
|
||||
Make sure you have the latest version of the Xcode command line tools installed:
|
||||
|
||||
```sh
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
||||
|
||||
# Available Actions
|
||||
|
||||
## Android
|
||||
|
||||
### android beta
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android beta
|
||||
```
|
||||
|
||||
Deploy to play store
|
||||
|
||||
### android build_github
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android build_github
|
||||
```
|
||||
|
||||
Deploy to github release
|
||||
|
||||
### android build
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android build
|
||||
```
|
||||
|
||||
Compile the project
|
||||
|
||||
### android update_version
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android update_version
|
||||
```
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
## iOS
|
||||
|
||||
### ios beta
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane ios beta
|
||||
```
|
||||
|
||||
Deploy to testflight
|
||||
|
||||
### ios build
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane ios build
|
||||
```
|
||||
|
||||
Compile the project
|
||||
|
||||
### ios provision
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane ios provision
|
||||
```
|
||||
|
||||
Install provisioning profiles using match
|
||||
|
||||
### ios update_version
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane ios update_version
|
||||
```
|
||||
|
||||
|
||||
|
||||
### ios testflight_prune_dry
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane ios testflight_prune_dry
|
||||
```
|
||||
|
||||
|
||||
|
||||
### ios testflight_prune
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane ios testflight_prune
|
||||
```
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
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).
|
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.120.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.131.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
63
osu.Android/AndroidImportTask.cs
Normal file
63
osu.Android/AndroidImportTask.cs
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Android.Content;
|
||||
using Android.Net;
|
||||
using Android.Provider;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Android
|
||||
{
|
||||
public class AndroidImportTask : ImportTask
|
||||
{
|
||||
private readonly ContentResolver contentResolver;
|
||||
|
||||
private readonly Uri uri;
|
||||
|
||||
private AndroidImportTask(Stream stream, string filename, ContentResolver contentResolver, Uri uri)
|
||||
: base(stream, filename)
|
||||
{
|
||||
this.contentResolver = contentResolver;
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public override void DeleteFile()
|
||||
{
|
||||
contentResolver.Delete(uri, null, null);
|
||||
}
|
||||
|
||||
public static async Task<AndroidImportTask?> Create(ContentResolver contentResolver, Uri uri)
|
||||
{
|
||||
// there are more performant overloads of this method, but this one is the most backwards-compatible
|
||||
// (dates back to API 1).
|
||||
|
||||
var cursor = contentResolver.Query(uri, null, null, null, null);
|
||||
|
||||
if (cursor == null)
|
||||
return null;
|
||||
|
||||
if (!cursor.MoveToFirst())
|
||||
return null;
|
||||
|
||||
int filenameColumn = cursor.GetColumnIndex(IOpenableColumns.DisplayName);
|
||||
string filename = cursor.GetString(filenameColumn) ?? uri.Path ?? string.Empty;
|
||||
|
||||
// SharpCompress requires archive streams to be seekable, which the stream opened by
|
||||
// OpenInputStream() seems to not necessarily be.
|
||||
// copy to an arbitrary-access memory stream to be able to proceed with the import.
|
||||
var copy = new MemoryStream();
|
||||
|
||||
using (var stream = contentResolver.OpenInputStream(uri))
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
await stream.CopyToAsync(copy).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return new AndroidImportTask(copy, filename, contentResolver, uri);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
@ -14,7 +13,6 @@ using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.Graphics;
|
||||
using Android.OS;
|
||||
using Android.Provider;
|
||||
using Android.Views;
|
||||
using osu.Framework.Android;
|
||||
using osu.Game.Database;
|
||||
@ -131,28 +129,14 @@ namespace osu.Android
|
||||
|
||||
await Task.WhenAll(uris.Select(async uri =>
|
||||
{
|
||||
// there are more performant overloads of this method, but this one is the most backwards-compatible
|
||||
// (dates back to API 1).
|
||||
var cursor = ContentResolver?.Query(uri, null, null, null, null);
|
||||
var task = await AndroidImportTask.Create(ContentResolver!, uri).ConfigureAwait(false);
|
||||
|
||||
if (cursor == null)
|
||||
return;
|
||||
|
||||
cursor.MoveToFirst();
|
||||
|
||||
int filenameColumn = cursor.GetColumnIndex(IOpenableColumns.DisplayName);
|
||||
string filename = cursor.GetString(filenameColumn);
|
||||
|
||||
// SharpCompress requires archive streams to be seekable, which the stream opened by
|
||||
// OpenInputStream() seems to not necessarily be.
|
||||
// copy to an arbitrary-access memory stream to be able to proceed with the import.
|
||||
var copy = new MemoryStream();
|
||||
using (var stream = ContentResolver.OpenInputStream(uri))
|
||||
await stream.CopyToAsync(copy).ConfigureAwait(false);
|
||||
|
||||
lock (tasks)
|
||||
if (task != null)
|
||||
{
|
||||
tasks.Add(new ImportTask(copy, filename));
|
||||
lock (tasks)
|
||||
{
|
||||
tasks.Add(task);
|
||||
}
|
||||
}
|
||||
})).ConfigureAwait(false);
|
||||
|
||||
|
@ -29,6 +29,7 @@ namespace osu.Desktop
|
||||
internal partial class OsuGameDesktop : OsuGame
|
||||
{
|
||||
private OsuSchemeLinkIPCChannel? osuSchemeLinkIPCChannel;
|
||||
private ArchiveImportIPCChannel? archiveImportIPCChannel;
|
||||
|
||||
public OsuGameDesktop(string[]? args = null)
|
||||
: base(args)
|
||||
@ -123,6 +124,7 @@ namespace osu.Desktop
|
||||
LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
|
||||
|
||||
osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this);
|
||||
archiveImportIPCChannel = new ArchiveImportIPCChannel(Host, this);
|
||||
}
|
||||
|
||||
public override void SetHost(GameHost host)
|
||||
@ -181,6 +183,7 @@ namespace osu.Desktop
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
osuSchemeLinkIPCChannel?.Dispose();
|
||||
archiveImportIPCChannel?.Dispose();
|
||||
}
|
||||
|
||||
private class SDL2BatteryInfo : BatteryInfo
|
||||
|
@ -376,7 +376,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
protected override void OnFree()
|
||||
{
|
||||
slidingSample.Samples = null;
|
||||
slidingSample.ClearSamples();
|
||||
base.OnFree();
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,11 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
@ -19,8 +19,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
private readonly Box colouredBox;
|
||||
private readonly Box shadow;
|
||||
private readonly Box shadeBackground;
|
||||
private readonly Box shadeForeground;
|
||||
|
||||
public ArgonHoldNoteTailPiece()
|
||||
{
|
||||
@ -32,32 +32,25 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
shadow = new Box
|
||||
shadeBackground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.82f,
|
||||
Masking = true,
|
||||
Height = ArgonNotePiece.NOTE_ACCENT_RATIO,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
colouredBox = new Box
|
||||
shadeForeground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = ArgonNotePiece.CORNER_RADIUS * 2,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -77,19 +70,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
{
|
||||
colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up
|
||||
? Anchor.TopCentre
|
||||
: Anchor.BottomCentre;
|
||||
Scale = new Vector2(1, direction.NewValue == ScrollingDirection.Up ? -1 : 1);
|
||||
}
|
||||
|
||||
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
||||
{
|
||||
colouredBox.Colour = ColourInfo.GradientVertical(
|
||||
accent.NewValue,
|
||||
accent.NewValue.Darken(0.1f)
|
||||
);
|
||||
|
||||
shadow.Colour = accent.NewValue.Darken(0.5f);
|
||||
shadeBackground.Colour = accent.NewValue.Darken(1.7f);
|
||||
shadeForeground.Colour = accent.NewValue.Darken(1.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
internal partial class ArgonNotePiece : CompositeDrawable
|
||||
{
|
||||
public const float NOTE_HEIGHT = 42;
|
||||
|
||||
public const float NOTE_ACCENT_RATIO = 0.82f;
|
||||
public const float CORNER_RADIUS = 3.4f;
|
||||
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.82f,
|
||||
Height = NOTE_ACCENT_RATIO,
|
||||
Masking = true,
|
||||
CornerRadius = CORNER_RADIUS,
|
||||
Children = new Drawable[]
|
||||
@ -95,6 +95,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up
|
||||
? Anchor.TopCentre
|
||||
: Anchor.BottomCentre;
|
||||
|
||||
Scale = new Vector2(1, direction.NewValue == ScrollingDirection.Up ? -1 : 1);
|
||||
}
|
||||
|
||||
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
||||
|
@ -4,29 +4,38 @@
|
||||
#nullable disable
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public partial class TestSceneHitCircleKiai : TestSceneHitCircle
|
||||
public partial class TestSceneHitCircleKiai : TestSceneHitCircle, IBeatSyncProvider
|
||||
{
|
||||
private ControlPointInfo controlPoints { get; set; }
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
var controlPointInfo = new ControlPointInfo();
|
||||
controlPoints = new ControlPointInfo();
|
||||
|
||||
controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||
controlPoints.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||
|
||||
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
|
||||
{
|
||||
ControlPointInfo = controlPointInfo
|
||||
ControlPointInfo = controlPoints
|
||||
});
|
||||
|
||||
// track needs to be playing for BeatSyncedContainer to work.
|
||||
Beatmap.Value.Track.Start();
|
||||
});
|
||||
|
||||
ChannelAmplitudes IHasAmplitudes.CurrentAmplitudes => new ChannelAmplitudes();
|
||||
ControlPointInfo IBeatSyncProvider.ControlPoints => controlPoints;
|
||||
IClock IBeatSyncProvider.Clock => Clock;
|
||||
}
|
||||
}
|
||||
|
@ -187,28 +187,19 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
if (b.IsSelected)
|
||||
continue;
|
||||
|
||||
var hitObject = (OsuHitObject)b.Item;
|
||||
var snapPositions = b.ScreenSpaceSnapPoints;
|
||||
|
||||
Vector2? snap = checkSnap(hitObject.Position);
|
||||
if (snap == null && hitObject.Position != hitObject.EndPosition)
|
||||
snap = checkSnap(hitObject.EndPosition);
|
||||
if (!snapPositions.Any())
|
||||
continue;
|
||||
|
||||
if (snap != null)
|
||||
var closestSnapPosition = snapPositions.MinBy(p => Vector2.Distance(p, screenSpacePosition));
|
||||
|
||||
if (Vector2.Distance(closestSnapPosition, screenSpacePosition) < snapRadius)
|
||||
{
|
||||
// only return distance portion, since time is not really valid
|
||||
snapResult = new SnapResult(snap.Value, null, playfield);
|
||||
snapResult = new SnapResult(closestSnapPosition, null, playfield);
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector2? checkSnap(Vector2 checkPos)
|
||||
{
|
||||
Vector2 checkScreenPos = playfield.GamefieldToScreenSpace(checkPos);
|
||||
|
||||
if (Vector2.Distance(checkScreenPos, screenSpacePosition) < snapRadius)
|
||||
return checkScreenPos;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
snapResult = null;
|
||||
|
@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
PathVersion.UnbindFrom(HitObject.Path.Version);
|
||||
|
||||
slidingSample.Samples = null;
|
||||
slidingSample?.ClearSamples();
|
||||
}
|
||||
|
||||
protected override void LoadSamples()
|
||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.OnFree();
|
||||
|
||||
spinningSample.Samples = null;
|
||||
spinningSample.ClearSamples();
|
||||
}
|
||||
|
||||
protected override void LoadSamples()
|
||||
|
@ -44,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||
private readonly FlashPiece flash;
|
||||
private readonly Container kiaiContainer;
|
||||
|
||||
private Bindable<bool> configHitLighting = null!;
|
||||
|
||||
@ -82,6 +83,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
kiaiContainer = new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = Size,
|
||||
Child = new KiaiFlash
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
},
|
||||
number = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.Default.With(size: 52, weight: FontWeight.Bold),
|
||||
@ -119,6 +131,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
outerGradient.ClearTransforms(targetMember: nameof(Colour));
|
||||
outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f));
|
||||
|
||||
kiaiContainer.Colour = colour.NewValue;
|
||||
outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4);
|
||||
innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f));
|
||||
flash.Colour = colour.NewValue;
|
||||
@ -151,10 +164,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
|
||||
const float shrink_size = 0.8f;
|
||||
|
||||
// When the user has hit lighting disabled, we won't be showing the bright white flash.
|
||||
// To make things look good, the surrounding animations are also slightly adjusted.
|
||||
bool showFlash = configHitLighting.Value;
|
||||
|
||||
// Animating with the number present is distracting.
|
||||
// The number disappearing is hidden by the bright flash.
|
||||
number.FadeOut(flash_in_duration / 2);
|
||||
@ -180,31 +189,39 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
// gradient layers.
|
||||
border.ResizeTo(Size * shrink_size + new Vector2(border.BorderThickness), resize_duration, Easing.OutElasticHalf);
|
||||
|
||||
// Kiai flash should track the overall size but also be cleaned up quite fast, so we don't get additional
|
||||
// flashes after the hit animation is already in a mostly-completed state.
|
||||
kiaiContainer.ResizeTo(Size * shrink_size, resize_duration, Easing.OutElasticHalf);
|
||||
kiaiContainer.FadeOut(flash_in_duration, Easing.OutQuint);
|
||||
|
||||
// The outer gradient is resize with a slight delay from the border.
|
||||
// This is to give it a bomb-like effect, with the border "triggering" its animation when getting close.
|
||||
using (BeginDelayedSequence(flash_in_duration / 12))
|
||||
{
|
||||
outerGradient.ResizeTo(OUTER_GRADIENT_SIZE * shrink_size, resize_duration, Easing.OutElasticHalf);
|
||||
|
||||
if (showFlash)
|
||||
{
|
||||
outerGradient
|
||||
.FadeColour(Color4.White, 80)
|
||||
.Then()
|
||||
.FadeOut(flash_in_duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
outerGradient
|
||||
.FadeColour(Color4.White, flash_in_duration * 8)
|
||||
.FadeOut(flash_in_duration * 2);
|
||||
}
|
||||
outerGradient
|
||||
.FadeColour(Color4.White, 80)
|
||||
.Then()
|
||||
.FadeOut(flash_in_duration);
|
||||
}
|
||||
|
||||
if (showFlash)
|
||||
if (configHitLighting.Value)
|
||||
{
|
||||
flash.HitLighting = true;
|
||||
flash.FadeTo(1, flash_in_duration, Easing.OutQuint);
|
||||
|
||||
this.FadeOut(showFlash ? fade_out_time : fade_out_time / 2, Easing.OutQuad);
|
||||
this.FadeOut(fade_out_time, Easing.OutQuad);
|
||||
}
|
||||
else
|
||||
{
|
||||
flash.HitLighting = false;
|
||||
flash.FadeTo(1, flash_in_duration, Easing.OutQuint)
|
||||
.Then()
|
||||
.FadeOut(flash_in_duration, Easing.OutQuint);
|
||||
|
||||
this.FadeOut(fade_out_time * 0.8f, Easing.OutQuad);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@ -236,6 +253,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
Child.AlwaysPresent = true;
|
||||
}
|
||||
|
||||
public bool HitLighting { get; set; }
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -244,7 +263,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Colour,
|
||||
Radius = OsuHitObject.OBJECT_RADIUS * 1.2f,
|
||||
Radius = OsuHitObject.OBJECT_RADIUS * (HitLighting ? 1.2f : 0.6f),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Taiko.Configuration;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
@ -14,36 +17,48 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
private DrumTouchInputArea drumTouchInputArea = null!;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
private readonly Bindable<TaikoTouchControlScheme> controlScheme = new Bindable<TaikoTouchControlScheme>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddStep("create drum", () =>
|
||||
var config = (TaikoRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull();
|
||||
config.BindWith(TaikoRulesetSetting.TouchControlScheme, controlScheme);
|
||||
}
|
||||
|
||||
private void createDrum()
|
||||
{
|
||||
Child = new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
||||
{
|
||||
Child = new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
new InputDrum
|
||||
{
|
||||
new InputDrum
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Height = 0.2f,
|
||||
},
|
||||
drumTouchInputArea = new DrumTouchInputArea
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
},
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Height = 0.2f,
|
||||
},
|
||||
};
|
||||
});
|
||||
drumTouchInputArea = new DrumTouchInputArea
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDrum()
|
||||
{
|
||||
AddStep("create drum", createDrum);
|
||||
AddStep("show drum", () => drumTouchInputArea.Show());
|
||||
|
||||
AddStep("change scheme (kddk)", () => controlScheme.Value = TaikoTouchControlScheme.KDDK);
|
||||
AddStep("change scheme (kkdd)", () => controlScheme.Value = TaikoTouchControlScheme.KKDD);
|
||||
AddStep("change scheme (ddkk)", () => controlScheme.Value = TaikoTouchControlScheme.DDKK);
|
||||
}
|
||||
|
||||
protected override Ruleset CreateRuleset() => new TaikoRuleset();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Configuration
|
||||
{
|
||||
public class TaikoRulesetConfigManager : RulesetConfigManager<TaikoRulesetSetting>
|
||||
{
|
||||
public TaikoRulesetConfigManager(SettingsStore? settings, RulesetInfo ruleset, int? variant = null)
|
||||
: base(settings, ruleset, variant)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void InitialiseDefaults()
|
||||
{
|
||||
base.InitialiseDefaults();
|
||||
|
||||
SetDefault(TaikoRulesetSetting.TouchControlScheme, TaikoTouchControlScheme.KDDK);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TaikoRulesetSetting
|
||||
{
|
||||
TouchControlScheme
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Configuration
|
||||
{
|
||||
public enum TaikoTouchControlScheme
|
||||
{
|
||||
KDDK,
|
||||
DDKK,
|
||||
KKDD
|
||||
}
|
||||
}
|
@ -28,9 +28,13 @@ using osu.Game.Rulesets.Taiko.Skinning.Argon;
|
||||
using osu.Game.Rulesets.Taiko.Skinning.Legacy;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking.Statistics;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Taiko.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko
|
||||
{
|
||||
@ -194,6 +198,10 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
||||
|
||||
public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new TaikoRulesetConfigManager(settings, RulesetInfo);
|
||||
|
||||
public override RulesetSettingsSubsection CreateSettings() => new TaikoSettingsSubsection(this);
|
||||
|
||||
protected override IEnumerable<HitResult> GetValidHitResults()
|
||||
{
|
||||
return new[]
|
||||
|
36
osu.Game.Rulesets.Taiko/TaikoSettingsSubsection.cs
Normal file
36
osu.Game.Rulesets.Taiko/TaikoSettingsSubsection.cs
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Taiko.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko
|
||||
{
|
||||
public partial class TaikoSettingsSubsection : RulesetSettingsSubsection
|
||||
{
|
||||
protected override LocalisableString Header => "osu!taiko";
|
||||
|
||||
public TaikoSettingsSubsection(TaikoRuleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var config = (TaikoRulesetConfigManager)Config;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsEnumDropdown<TaikoTouchControlScheme>
|
||||
{
|
||||
LabelText = "Touch control scheme",
|
||||
Current = config.GetBindable<TaikoTouchControlScheme>(TaikoRulesetSetting.TouchControlScheme)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -11,6 +13,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Configuration;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -31,15 +34,18 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
private Container mainContent = null!;
|
||||
|
||||
private QuarterCircle leftCentre = null!;
|
||||
private QuarterCircle rightCentre = null!;
|
||||
private QuarterCircle leftRim = null!;
|
||||
private QuarterCircle rightRim = null!;
|
||||
private DrumSegment leftCentre = null!;
|
||||
private DrumSegment rightCentre = null!;
|
||||
private DrumSegment leftRim = null!;
|
||||
private DrumSegment rightRim = null!;
|
||||
|
||||
private readonly Bindable<TaikoTouchControlScheme> configTouchControlScheme = new Bindable<TaikoTouchControlScheme>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TaikoInputManager taikoInputManager, OsuColour colours)
|
||||
private void load(TaikoInputManager taikoInputManager, TaikoRulesetConfigManager config)
|
||||
{
|
||||
Debug.Assert(taikoInputManager.KeyBindingContainer != null);
|
||||
|
||||
keyBindingContainer = taikoInputManager.KeyBindingContainer;
|
||||
|
||||
// Container should handle input everywhere.
|
||||
@ -65,27 +71,27 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
leftRim = new QuarterCircle(TaikoAction.LeftRim, colours.Blue)
|
||||
leftRim = new DrumSegment
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomRight,
|
||||
X = -2,
|
||||
},
|
||||
rightRim = new QuarterCircle(TaikoAction.RightRim, colours.Blue)
|
||||
rightRim = new DrumSegment
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomRight,
|
||||
X = 2,
|
||||
Rotation = 90,
|
||||
},
|
||||
leftCentre = new QuarterCircle(TaikoAction.LeftCentre, colours.Pink)
|
||||
leftCentre = new DrumSegment
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomRight,
|
||||
X = -2,
|
||||
Scale = new Vector2(centre_region),
|
||||
},
|
||||
rightCentre = new QuarterCircle(TaikoAction.RightCentre, colours.Pink)
|
||||
rightCentre = new DrumSegment
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomRight,
|
||||
@ -98,6 +104,17 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
config.BindWith(TaikoRulesetSetting.TouchControlScheme, configTouchControlScheme);
|
||||
configTouchControlScheme.BindValueChanged(scheme =>
|
||||
{
|
||||
var actions = getOrderedActionsForScheme(scheme.NewValue);
|
||||
|
||||
leftRim.Action = actions[0];
|
||||
leftCentre.Action = actions[1];
|
||||
rightCentre.Action = actions[2];
|
||||
rightRim.Action = actions[3];
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
@ -119,11 +136,47 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
base.OnTouchUp(e);
|
||||
}
|
||||
|
||||
private static TaikoAction[] getOrderedActionsForScheme(TaikoTouchControlScheme scheme)
|
||||
{
|
||||
switch (scheme)
|
||||
{
|
||||
case TaikoTouchControlScheme.KDDK:
|
||||
return new[]
|
||||
{
|
||||
TaikoAction.LeftRim,
|
||||
TaikoAction.LeftCentre,
|
||||
TaikoAction.RightCentre,
|
||||
TaikoAction.RightRim
|
||||
};
|
||||
|
||||
case TaikoTouchControlScheme.DDKK:
|
||||
return new[]
|
||||
{
|
||||
TaikoAction.LeftCentre,
|
||||
TaikoAction.RightCentre,
|
||||
TaikoAction.LeftRim,
|
||||
TaikoAction.RightRim
|
||||
};
|
||||
|
||||
case TaikoTouchControlScheme.KKDD:
|
||||
return new[]
|
||||
{
|
||||
TaikoAction.LeftRim,
|
||||
TaikoAction.RightRim,
|
||||
TaikoAction.LeftCentre,
|
||||
TaikoAction.RightCentre
|
||||
};
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(scheme), scheme, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDown(object source, Vector2 position)
|
||||
{
|
||||
Show();
|
||||
|
||||
TaikoAction taikoAction = getTaikoActionFromInput(position);
|
||||
TaikoAction taikoAction = getTaikoActionFromPosition(position);
|
||||
|
||||
// Not too sure how this can happen, but let's avoid throwing.
|
||||
if (trackedActions.ContainsKey(source))
|
||||
@ -139,18 +192,15 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
trackedActions.Remove(source);
|
||||
}
|
||||
|
||||
private bool validMouse(MouseButtonEvent e) =>
|
||||
leftRim.Contains(e.ScreenSpaceMouseDownPosition) || rightRim.Contains(e.ScreenSpaceMouseDownPosition);
|
||||
|
||||
private TaikoAction getTaikoActionFromInput(Vector2 inputPosition)
|
||||
private TaikoAction getTaikoActionFromPosition(Vector2 inputPosition)
|
||||
{
|
||||
bool centreHit = leftCentre.Contains(inputPosition) || rightCentre.Contains(inputPosition);
|
||||
bool leftSide = ToLocalSpace(inputPosition).X < DrawWidth / 2;
|
||||
|
||||
if (leftSide)
|
||||
return centreHit ? TaikoAction.LeftCentre : TaikoAction.LeftRim;
|
||||
return centreHit ? leftCentre.Action : leftRim.Action;
|
||||
|
||||
return centreHit ? TaikoAction.RightCentre : TaikoAction.RightRim;
|
||||
return centreHit ? rightCentre.Action : rightRim.Action;
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
@ -163,23 +213,42 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
mainContent.FadeOut(300);
|
||||
}
|
||||
|
||||
private partial class QuarterCircle : CompositeDrawable, IKeyBindingHandler<TaikoAction>
|
||||
private partial class DrumSegment : CompositeDrawable, IKeyBindingHandler<TaikoAction>
|
||||
{
|
||||
private readonly Circle overlay;
|
||||
private TaikoAction action;
|
||||
|
||||
private readonly TaikoAction handledAction;
|
||||
public TaikoAction Action
|
||||
{
|
||||
get => action;
|
||||
set
|
||||
{
|
||||
if (action == value)
|
||||
return;
|
||||
|
||||
private readonly Circle circle;
|
||||
action = value;
|
||||
updateColoursFromAction();
|
||||
}
|
||||
}
|
||||
|
||||
private Circle overlay = null!;
|
||||
|
||||
private Circle circle = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public override bool Contains(Vector2 screenSpacePos) => circle.Contains(screenSpacePos);
|
||||
|
||||
public QuarterCircle(TaikoAction handledAction, Color4 colour)
|
||||
public DrumSegment()
|
||||
{
|
||||
this.handledAction = handledAction;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
FillMode = FillMode.Fit;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
@ -191,7 +260,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
circle = new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colour.Multiply(1.4f).Darken(2.8f),
|
||||
Alpha = 0.8f,
|
||||
Scale = new Vector2(2),
|
||||
},
|
||||
@ -200,7 +268,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Colour = colour,
|
||||
Scale = new Vector2(2),
|
||||
}
|
||||
}
|
||||
@ -208,18 +275,52 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
updateColoursFromAction();
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
{
|
||||
if (e.Action == handledAction)
|
||||
if (e.Action == Action)
|
||||
overlay.FadeTo(1f, 80, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
||||
{
|
||||
if (e.Action == handledAction)
|
||||
if (e.Action == Action)
|
||||
overlay.FadeOut(1000, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void updateColoursFromAction()
|
||||
{
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
var colour = getColourFromTaikoAction(Action);
|
||||
|
||||
circle.Colour = colour.Multiply(1.4f).Darken(2.8f);
|
||||
overlay.Colour = colour;
|
||||
}
|
||||
|
||||
private Color4 getColourFromTaikoAction(TaikoAction handledAction)
|
||||
{
|
||||
switch (handledAction)
|
||||
{
|
||||
case TaikoAction.LeftRim:
|
||||
case TaikoAction.RightRim:
|
||||
return colours.Blue;
|
||||
|
||||
case TaikoAction.LeftCentre:
|
||||
case TaikoAction.RightCentre:
|
||||
return colours.Pink;
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
88
osu.Game.Tests/Editing/Checks/CheckPreviewTimeTest.cs
Normal file
88
osu.Game.Tests/Editing/Checks/CheckPreviewTimeTest.cs
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Checks;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
public class CheckPreviewTimeTest
|
||||
{
|
||||
private CheckPreviewTime check = null!;
|
||||
|
||||
private IBeatmap beatmap = null!;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckPreviewTime();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPreviewTimeNotSet()
|
||||
{
|
||||
setNoPreviewTimeBeatmap();
|
||||
var content = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
var issues = check.Run(content).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckPreviewTime.IssueTemplateHasNoPreviewTime);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPreviewTimeconflict()
|
||||
{
|
||||
setPreviewTimeConflictBeatmap();
|
||||
|
||||
var content = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
var issues = check.Run(content).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckPreviewTime.IssueTemplatePreviewTimeConflict);
|
||||
Assert.That(issues.Single().Arguments.FirstOrDefault()?.ToString() == "Test1");
|
||||
}
|
||||
|
||||
private void setNoPreviewTimeBeatmap()
|
||||
{
|
||||
beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata { PreviewTime = -1 },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void setPreviewTimeConflictBeatmap()
|
||||
{
|
||||
beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata { PreviewTime = 10 },
|
||||
BeatmapSet = new BeatmapSetInfo(new List<BeatmapInfo>
|
||||
{
|
||||
new BeatmapInfo
|
||||
{
|
||||
DifficultyName = "Test1",
|
||||
Metadata = new BeatmapMetadata { PreviewTime = 5 },
|
||||
},
|
||||
new BeatmapInfo
|
||||
{
|
||||
DifficultyName = "Test2",
|
||||
Metadata = new BeatmapMetadata { PreviewTime = 10 },
|
||||
},
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
BIN
osu.Game.Tests/Resources/Archives/conflicting-filenames-skin.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/conflicting-filenames-skin.osk
Normal file
Binary file not shown.
@ -150,6 +150,8 @@ namespace osu.Game.Tests.Rulesets
|
||||
public IBindable<double> AggregateTempo => throw new NotImplementedException();
|
||||
|
||||
public int PlaybackConcurrency { get; set; }
|
||||
|
||||
public void AddExtension(string extension) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class TestShaderManager : ShaderManager
|
||||
|
@ -1,12 +1,11 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -20,29 +19,36 @@ namespace osu.Game.Tests.Skins
|
||||
public partial class TestSceneBeatmapSkinResources : OsuTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
private BeatmapManager beatmaps { get; set; } = null!;
|
||||
|
||||
private IWorkingBeatmap beatmap;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
[Test]
|
||||
public void TestRetrieveOggAudio()
|
||||
{
|
||||
var imported = beatmaps.Import(new ImportTask(TestResources.OpenResource("Archives/ogg-beatmap.osz"), "ogg-beatmap.osz")).GetResultSafely();
|
||||
IWorkingBeatmap beatmap = null!;
|
||||
|
||||
imported?.PerformRead(s =>
|
||||
AddStep("import beatmap", () => beatmap = importBeatmapFromArchives(@"ogg-beatmap.osz"));
|
||||
AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo(@"sample")) != null);
|
||||
AddAssert("track is non-null", () =>
|
||||
{
|
||||
beatmap = beatmaps.GetWorkingBeatmap(s.Beatmaps[0]);
|
||||
using (var track = beatmap.LoadTrack())
|
||||
return track is not TrackVirtual;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo("sample")) != null);
|
||||
|
||||
[Test]
|
||||
public void TestRetrieveOggTrack() => AddAssert("track is non-null", () =>
|
||||
public void TestRetrievalWithConflictingFilenames()
|
||||
{
|
||||
using (var track = beatmap.LoadTrack())
|
||||
return track is not TrackVirtual;
|
||||
});
|
||||
IWorkingBeatmap beatmap = null!;
|
||||
|
||||
AddStep("import beatmap", () => beatmap = importBeatmapFromArchives(@"conflicting-filenames-beatmap.osz"));
|
||||
AddAssert("texture is non-null", () => beatmap.Skin.GetTexture(@"spinner-osu") != null);
|
||||
AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo(@"spinner-osu")) != null);
|
||||
}
|
||||
|
||||
private IWorkingBeatmap importBeatmapFromArchives(string filename)
|
||||
{
|
||||
var imported = beatmaps.Import(new ImportTask(TestResources.OpenResource($@"Archives/{filename}"), filename)).GetResultSafely();
|
||||
return imported.AsNonNull().PerformRead(s => beatmaps.GetWorkingBeatmap(s.Beatmaps[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,17 +31,24 @@ namespace osu.Game.Tests.Skins
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; } = null!;
|
||||
|
||||
private ISkin skin = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
[Test]
|
||||
public void TestRetrieveOggSample()
|
||||
{
|
||||
var imported = skins.Import(new ImportTask(TestResources.OpenResource("Archives/ogg-skin.osk"), "ogg-skin.osk")).GetResultSafely();
|
||||
skin = imported.PerformRead(skinInfo => skins.GetSkin(skinInfo));
|
||||
ISkin skin = null!;
|
||||
|
||||
AddStep("import skin", () => skin = importSkinFromArchives(@"ogg-skin.osk"));
|
||||
AddAssert("sample is non-null", () => skin.GetSample(new SampleInfo(@"sample")) != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => skin.GetSample(new SampleInfo("sample")) != null);
|
||||
public void TestRetrievalWithConflictingFilenames()
|
||||
{
|
||||
ISkin skin = null!;
|
||||
|
||||
AddStep("import skin", () => skin = importSkinFromArchives(@"conflicting-filenames-skin.osk"));
|
||||
AddAssert("texture is non-null", () => skin.GetTexture(@"spinner-osu") != null);
|
||||
AddAssert("sample is non-null", () => skin.GetSample(new SampleInfo(@"spinner-osu")) != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSampleRetrievalOrder()
|
||||
@ -78,6 +85,12 @@ namespace osu.Game.Tests.Skins
|
||||
});
|
||||
}
|
||||
|
||||
private Skin importSkinFromArchives(string filename)
|
||||
{
|
||||
var imported = skins.Import(new ImportTask(TestResources.OpenResource($@"Archives/{filename}"), filename)).GetResultSafely();
|
||||
return imported.PerformRead(skinInfo => skins.GetSkin(skinInfo));
|
||||
}
|
||||
|
||||
private class TestSkin : Skin
|
||||
{
|
||||
public const string SAMPLE_NAME = "test-sample";
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
{
|
||||
public partial class TestSceneTriangleBorderShader : OsuTestScene
|
||||
{
|
||||
private readonly TriangleBorder border;
|
||||
private readonly TestTriangle triangle;
|
||||
|
||||
public TestSceneTriangleBorderShader()
|
||||
{
|
||||
@ -25,11 +25,11 @@ namespace osu.Game.Tests.Visual.Background
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.DarkGreen
|
||||
},
|
||||
border = new TriangleBorder
|
||||
triangle = new TestTriangle
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(100)
|
||||
Size = new Vector2(200)
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -38,12 +38,13 @@ namespace osu.Game.Tests.Visual.Background
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AddSliderStep("Thickness", 0f, 1f, 0.02f, t => border.Thickness = t);
|
||||
AddSliderStep("Thickness", 0f, 1f, 0.15f, t => triangle.Thickness = t);
|
||||
AddSliderStep("Texel size", 0f, 0.1f, 0f, t => triangle.TexelSize = t);
|
||||
}
|
||||
|
||||
private partial class TriangleBorder : Sprite
|
||||
private partial class TestTriangle : Sprite
|
||||
{
|
||||
private float thickness = 0.02f;
|
||||
private float thickness = 0.15f;
|
||||
|
||||
public float Thickness
|
||||
{
|
||||
@ -55,6 +56,18 @@ namespace osu.Game.Tests.Visual.Background
|
||||
}
|
||||
}
|
||||
|
||||
private float texelSize;
|
||||
|
||||
public float TexelSize
|
||||
{
|
||||
get => texelSize;
|
||||
set
|
||||
{
|
||||
texelSize = value;
|
||||
Invalidate(Invalidation.DrawNode);
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ShaderManager shaders, IRenderer renderer)
|
||||
{
|
||||
@ -62,29 +75,32 @@ namespace osu.Game.Tests.Visual.Background
|
||||
Texture = renderer.WhitePixel;
|
||||
}
|
||||
|
||||
protected override DrawNode CreateDrawNode() => new TriangleBorderDrawNode(this);
|
||||
protected override DrawNode CreateDrawNode() => new TriangleDrawNode(this);
|
||||
|
||||
private class TriangleBorderDrawNode : SpriteDrawNode
|
||||
private class TriangleDrawNode : SpriteDrawNode
|
||||
{
|
||||
public new TriangleBorder Source => (TriangleBorder)base.Source;
|
||||
public new TestTriangle Source => (TestTriangle)base.Source;
|
||||
|
||||
public TriangleBorderDrawNode(TriangleBorder source)
|
||||
public TriangleDrawNode(TestTriangle source)
|
||||
: base(source)
|
||||
{
|
||||
}
|
||||
|
||||
private float thickness;
|
||||
private float texelSize;
|
||||
|
||||
public override void ApplyState()
|
||||
{
|
||||
base.ApplyState();
|
||||
|
||||
thickness = Source.thickness;
|
||||
texelSize = Source.texelSize;
|
||||
}
|
||||
|
||||
public override void Draw(IRenderer renderer)
|
||||
{
|
||||
TextureShader.GetUniform<float>("thickness").UpdateValue(ref thickness);
|
||||
TextureShader.GetUniform<float>("texelSize").UpdateValue(ref texelSize);
|
||||
|
||||
base.Draw(renderer);
|
||||
}
|
||||
|
@ -8,12 +8,14 @@ using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Background
|
||||
{
|
||||
public partial class TestSceneTrianglesV2Background : OsuTestScene
|
||||
{
|
||||
private readonly TrianglesV2 triangles;
|
||||
private readonly TrianglesV2 maskedTriangles;
|
||||
private readonly Box box;
|
||||
|
||||
public TestSceneTrianglesV2Background()
|
||||
@ -31,12 +33,20 @@ namespace osu.Game.Tests.Visual.Background
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5),
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "Masked"
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Size = new Vector2(500, 100),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
CornerRadius = 40,
|
||||
Children = new Drawable[]
|
||||
@ -54,9 +64,43 @@ namespace osu.Game.Tests.Visual.Background
|
||||
}
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "Non-masked"
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Size = new Vector2(500, 100),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Red
|
||||
},
|
||||
maskedTriangles = new TrianglesV2
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "Gradient comparison box"
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Size = new Vector2(500, 100),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
CornerRadius = 40,
|
||||
Child = box = new Box
|
||||
@ -75,14 +119,16 @@ namespace osu.Game.Tests.Visual.Background
|
||||
|
||||
AddSliderStep("Spawn ratio", 0f, 10f, 1f, s =>
|
||||
{
|
||||
triangles.SpawnRatio = s;
|
||||
triangles.SpawnRatio = maskedTriangles.SpawnRatio = s;
|
||||
triangles.Reset(1234);
|
||||
maskedTriangles.Reset(1234);
|
||||
});
|
||||
AddSliderStep("Thickness", 0f, 1f, 0.02f, t => triangles.Thickness = t);
|
||||
AddSliderStep("Thickness", 0f, 1f, 0.02f, t => triangles.Thickness = maskedTriangles.Thickness = t);
|
||||
|
||||
AddStep("White colour", () => box.Colour = triangles.Colour = Color4.White);
|
||||
AddStep("Vertical gradient", () => box.Colour = triangles.Colour = ColourInfo.GradientVertical(Color4.White, Color4.Red));
|
||||
AddStep("Horizontal gradient", () => box.Colour = triangles.Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Red));
|
||||
AddStep("White colour", () => box.Colour = triangles.Colour = maskedTriangles.Colour = Color4.White);
|
||||
AddStep("Vertical gradient", () => box.Colour = triangles.Colour = maskedTriangles.Colour = ColourInfo.GradientVertical(Color4.White, Color4.Red));
|
||||
AddStep("Horizontal gradient", () => box.Colour = triangles.Colour = maskedTriangles.Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Red));
|
||||
AddToggleStep("Masking", m => maskedTriangles.Masking = m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
@ -89,7 +90,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Player.OnUpdate += _ =>
|
||||
{
|
||||
double currentTime = Player.GameplayClockContainer.CurrentTime;
|
||||
alwaysGoingForward &= currentTime >= lastTime - 500;
|
||||
bool goingForward = currentTime >= lastTime - 500;
|
||||
|
||||
alwaysGoingForward &= goingForward;
|
||||
|
||||
if (!goingForward)
|
||||
Logger.Log($"Backwards time occurred ({currentTime:N1} -> {lastTime:N1})");
|
||||
|
||||
lastTime = currentTime;
|
||||
};
|
||||
});
|
||||
|
@ -4,6 +4,7 @@
|
||||
#nullable disable
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osuTK.Input;
|
||||
@ -24,13 +25,40 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPause()
|
||||
public void TestPauseViaSpace()
|
||||
{
|
||||
double? lastTime = null;
|
||||
|
||||
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||
|
||||
AddStep("Pause playback", () => InputManager.Key(Key.Space));
|
||||
AddStep("Pause playback with space", () => InputManager.Key(Key.Space));
|
||||
|
||||
AddAssert("player not exited", () => Player.IsCurrentScreen());
|
||||
|
||||
AddUntilStep("Time stopped progressing", () =>
|
||||
{
|
||||
double current = Player.GameplayClockContainer.CurrentTime;
|
||||
bool changed = lastTime != current;
|
||||
lastTime = current;
|
||||
|
||||
return !changed;
|
||||
});
|
||||
|
||||
AddWaitStep("wait some", 10);
|
||||
|
||||
AddAssert("Time still stopped", () => lastTime == Player.GameplayClockContainer.CurrentTime);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPauseViaMiddleMouse()
|
||||
{
|
||||
double? lastTime = null;
|
||||
|
||||
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||
|
||||
AddStep("Pause playback with middle mouse", () => InputManager.Click(MouseButton.Middle));
|
||||
|
||||
AddAssert("player not exited", () => Player.IsCurrentScreen());
|
||||
|
||||
AddUntilStep("Time stopped progressing", () =>
|
||||
{
|
||||
|
@ -9,11 +9,11 @@ using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Skinning.Editor;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
@ -7,9 +7,9 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Skinning.Editor;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
|
@ -8,11 +8,11 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning.Editor;
|
||||
using osu.Game.Tests.Gameplay;
|
||||
using osuTK.Input;
|
||||
|
||||
|
@ -16,6 +16,7 @@ using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
@ -85,6 +86,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
|
||||
AddUntilStep("wait for join", () => MultiplayerClient.RoomJoined);
|
||||
AddUntilStep("wait for ongoing operation to complete", () => !(CurrentScreen as OnlinePlayScreen).ChildrenOfType<OngoingOperationTracker>().Single().InProgress.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -15,7 +15,6 @@ using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -44,14 +43,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* TearDown : System.TimeoutException : "wait for ongoing operation to complete" timed out
|
||||
* --TearDown
|
||||
* at osu.Framework.Testing.Drawables.Steps.UntilStepButton.<>c__DisplayClass11_0.<.ctor>b__0()
|
||||
* at osu.Framework.Testing.Drawables.Steps.StepButton.PerformStep(Boolean userTriggered)
|
||||
* at osu.Framework.Testing.TestScene.runNextStep(Action onCompletion, Action`1 onError, Func`2 stopCondition)
|
||||
*/
|
||||
public void TestItemAddedToTheEndOfQueue()
|
||||
{
|
||||
addItem(() => OtherBeatmap);
|
||||
@ -64,7 +55,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestNextItemSelectedAfterGameplayFinish()
|
||||
{
|
||||
addItem(() => OtherBeatmap);
|
||||
@ -82,7 +72,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestItemsNotClearedWhenSwitchToHostOnlyMode()
|
||||
{
|
||||
addItem(() => OtherBeatmap);
|
||||
@ -98,7 +87,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestCorrectItemSelectedAfterNewItemAdded()
|
||||
{
|
||||
addItem(() => OtherBeatmap);
|
||||
@ -106,7 +94,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestCorrectRulesetSelectedAfterNewItemAdded()
|
||||
{
|
||||
addItem(() => OtherBeatmap, new CatchRuleset().RulesetInfo);
|
||||
@ -124,7 +111,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestCorrectModsSelectedAfterNewItemAdded()
|
||||
{
|
||||
addItem(() => OtherBeatmap, mods: new Mod[] { new OsuModDoubleTime() });
|
||||
@ -153,7 +139,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddUntilStep("wait for song select", () => (songSelect = CurrentSubScreen as Screens.Select.SongSelect) != null);
|
||||
AddUntilStep("wait for loaded", () => songSelect.AsNonNull().BeatmapSetsLoaded);
|
||||
AddUntilStep("wait for ongoing operation to complete", () => !(CurrentScreen as OnlinePlayScreen).ChildrenOfType<OngoingOperationTracker>().Single().InProgress.Value);
|
||||
|
||||
if (ruleset != null)
|
||||
AddStep($"set {ruleset.Name} ruleset", () => songSelect.AsNonNull().Ruleset.Value = ruleset);
|
||||
|
@ -50,14 +50,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* TearDown : System.TimeoutException : "wait for ongoing operation to complete" timed out
|
||||
* --TearDown
|
||||
* at osu.Framework.Testing.Drawables.Steps.UntilStepButton.<>c__DisplayClass11_0.<.ctor>b__0()
|
||||
* at osu.Framework.Testing.Drawables.Steps.StepButton.PerformStep(Boolean userTriggered)
|
||||
* at osu.Framework.Testing.TestScene.runNextStep(Action onCompletion, Action`1 onError, Func`2 stopCondition)
|
||||
*/
|
||||
public void TestItemStillSelectedAfterChangeToSameBeatmap()
|
||||
{
|
||||
selectNewItem(() => InitialBeatmap);
|
||||
@ -66,7 +58,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestItemStillSelectedAfterChangeToOtherBeatmap()
|
||||
{
|
||||
selectNewItem(() => OtherBeatmap);
|
||||
@ -75,7 +66,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestOnlyLastItemChangedAfterGameplayFinished()
|
||||
{
|
||||
RunGameplay();
|
||||
@ -90,7 +80,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestAddItemsAsHost()
|
||||
{
|
||||
addItem(() => OtherBeatmap);
|
||||
@ -115,7 +104,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
|
||||
|
||||
BeatmapInfo otherBeatmap = null;
|
||||
AddUntilStep("wait for ongoing operation to complete", () => !(CurrentScreen as OnlinePlayScreen).ChildrenOfType<OngoingOperationTracker>().Single().InProgress.Value);
|
||||
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(otherBeatmap = beatmap()));
|
||||
|
||||
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
|
||||
@ -131,7 +119,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
});
|
||||
|
||||
AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
|
||||
AddUntilStep("wait for ongoing operation to complete", () => !(CurrentScreen as OnlinePlayScreen).ChildrenOfType<OngoingOperationTracker>().Single().InProgress.Value);
|
||||
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(beatmap()));
|
||||
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Overlays.Settings.Sections;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Skinning.Editor;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework;
|
||||
@ -14,6 +15,8 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
@ -23,11 +26,13 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
private HeadlessGameHost ipcSenderHost = null!;
|
||||
|
||||
private OsuSchemeLinkIPCChannel osuSchemeLinkIPCReceiver = null!;
|
||||
private OsuSchemeLinkIPCChannel osuSchemeLinkIPCSender = null!;
|
||||
private ArchiveImportIPCChannel archiveImportIPCSender = null!;
|
||||
|
||||
private const int requested_beatmap_set_id = 1;
|
||||
|
||||
protected override TestOsuGame CreateTestGame() => new IpcGame(LocalStorage, API);
|
||||
|
||||
[Resolved]
|
||||
private GameHost gameHost { get; set; } = null!;
|
||||
|
||||
@ -56,11 +61,11 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
return false;
|
||||
};
|
||||
});
|
||||
AddStep("create IPC receiver channel", () => osuSchemeLinkIPCReceiver = new OsuSchemeLinkIPCChannel(gameHost, Game));
|
||||
AddStep("create IPC sender channel", () =>
|
||||
AddStep("create IPC sender channels", () =>
|
||||
{
|
||||
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { BindIPC = true });
|
||||
osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost);
|
||||
archiveImportIPCSender = new ArchiveImportIPCChannel(ipcSenderHost);
|
||||
});
|
||||
}
|
||||
|
||||
@ -72,15 +77,50 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("beatmap overlay showing content", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.Header.BeatmapSet.Value.OnlineID == requested_beatmap_set_id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestArchiveImportLinkIPCChannel()
|
||||
{
|
||||
string? beatmapFilepath = null;
|
||||
|
||||
AddStep("import beatmap via IPC", () => archiveImportIPCSender.ImportAsync(beatmapFilepath = TestResources.GetQuickTestBeatmapForImport()).WaitSafely());
|
||||
AddUntilStep("import complete notification was presented", () => Game.Notifications.ChildrenOfType<ProgressCompletionNotification>().Count(), () => Is.EqualTo(1));
|
||||
AddAssert("original file deleted", () => File.Exists(beatmapFilepath), () => Is.False);
|
||||
}
|
||||
|
||||
public override void TearDownSteps()
|
||||
{
|
||||
AddStep("dispose IPC receiver", () => osuSchemeLinkIPCReceiver.Dispose());
|
||||
AddStep("dispose IPC sender", () =>
|
||||
AddStep("dispose IPC senders", () =>
|
||||
{
|
||||
osuSchemeLinkIPCSender.Dispose();
|
||||
archiveImportIPCSender.Dispose();
|
||||
ipcSenderHost.Dispose();
|
||||
});
|
||||
base.TearDownSteps();
|
||||
}
|
||||
|
||||
private partial class IpcGame : TestOsuGame
|
||||
{
|
||||
private OsuSchemeLinkIPCChannel? osuSchemeLinkIPCChannel;
|
||||
private ArchiveImportIPCChannel? archiveImportIPCChannel;
|
||||
|
||||
public IpcGame(Storage storage, IAPIProvider api, string[]? args = null)
|
||||
: base(storage, api, args)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this);
|
||||
archiveImportIPCChannel = new ArchiveImportIPCChannel(Host, this);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
osuSchemeLinkIPCChannel?.Dispose();
|
||||
archiveImportIPCChannel?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -563,6 +563,18 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("featured artist filter is off", () => !getBeatmapListingOverlay().ChildrenOfType<BeatmapSearchGeneralFilterRow>().First().Current.Contains(SearchGeneral.FeaturedArtists));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapListingLinkSearchOnInitialOpen()
|
||||
{
|
||||
BeatmapListingOverlay getBeatmapListingOverlay() => Game.ChildrenOfType<BeatmapListingOverlay>().FirstOrDefault();
|
||||
|
||||
AddStep("open beatmap overlay with test query", () => Game.SearchBeatmapSet("test"));
|
||||
|
||||
AddUntilStep("wait for beatmap overlay to load", () => getBeatmapListingOverlay()?.State.Value == Visibility.Visible);
|
||||
|
||||
AddAssert("beatmap overlay sorted by relevance", () => getBeatmapListingOverlay().ChildrenOfType<BeatmapListingSortTabControl>().Single().Current.Value == SortCriteria.Relevance);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMainOverlaysClosesNotificationOverlay()
|
||||
{
|
||||
|
@ -12,11 +12,11 @@ using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||
using osu.Game.Skinning.Editor;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
92
osu.Game.Tests/Visual/Online/TestSceneGroupBadges.cs
Normal file
92
osu.Game.Tests/Visual/Online/TestSceneGroupBadges.cs
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.Profile.Header.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
[TestFixture]
|
||||
public partial class TestSceneGroupBadges : OsuTestScene
|
||||
{
|
||||
public TestSceneGroupBadges()
|
||||
{
|
||||
var groups = new[]
|
||||
{
|
||||
new APIUser(),
|
||||
new APIUser
|
||||
{
|
||||
Groups = new[]
|
||||
{
|
||||
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||
}
|
||||
},
|
||||
new APIUser
|
||||
{
|
||||
Groups = new[]
|
||||
{
|
||||
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
|
||||
}
|
||||
},
|
||||
new APIUser
|
||||
{
|
||||
Groups = new[]
|
||||
{
|
||||
new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" },
|
||||
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
|
||||
}
|
||||
},
|
||||
new APIUser
|
||||
{
|
||||
Groups = new[]
|
||||
{
|
||||
new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" },
|
||||
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||
new APIUserGroup { Colour = "#999999", ShortName = "ALM", Name = "osu! Alumni" },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators (Probationary)", Playmodes = new[] { "osu", "taiko" }, IsProbationary = true }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Colour4.DarkGray
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(40),
|
||||
Children = new[]
|
||||
{
|
||||
new FillFlowContainer<GroupBadgeFlow>
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(5),
|
||||
ChildrenEnumerable = groups.Select(g => new GroupBadgeFlow { User = { Value = g } })
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -90,7 +90,9 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "mania" } },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko", "fruits", "mania" } },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators (Probationary)", Playmodes = new[] { "osu", "taiko", "fruits", "mania" }, IsProbationary = true }
|
||||
},
|
||||
ProfileOrder = new[]
|
||||
{
|
||||
|
@ -268,7 +268,10 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
Stream storyboardFileStream = null;
|
||||
|
||||
if (BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename is string storyboardFilename)
|
||||
string mainStoryboardFilename = getMainStoryboardFilename(BeatmapSetInfo.Metadata);
|
||||
|
||||
if (BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.Equals(mainStoryboardFilename, StringComparison.OrdinalIgnoreCase))?.Filename is string
|
||||
storyboardFilename)
|
||||
{
|
||||
string storyboardFileStorePath = BeatmapSetInfo?.GetPathForFile(storyboardFilename);
|
||||
storyboardFileStream = GetStream(storyboardFileStorePath);
|
||||
@ -312,6 +315,33 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
|
||||
public override Stream GetStream(string storagePath) => resources.Files.GetStream(storagePath);
|
||||
|
||||
private string getMainStoryboardFilename(IBeatmapMetadataInfo metadata)
|
||||
{
|
||||
// Matches stable implementation, because it's probably simpler than trying to do anything else.
|
||||
// This may need to be reconsidered after we begin storing storyboards in the new editor.
|
||||
return windowsFilenameStrip(
|
||||
(metadata.Artist.Length > 0 ? metadata.Artist + @" - " + metadata.Title : Path.GetFileNameWithoutExtension(metadata.AudioFile))
|
||||
+ (metadata.Author.Username.Length > 0 ? @" (" + metadata.Author.Username + @")" : string.Empty)
|
||||
+ @".osb");
|
||||
|
||||
string windowsFilenameStrip(string entry)
|
||||
{
|
||||
// Inlined from Path.GetInvalidFilenameChars() to ensure the windows characters are used (to match stable).
|
||||
char[] invalidCharacters =
|
||||
{
|
||||
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
|
||||
'\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12',
|
||||
'\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D',
|
||||
'\x1E', '\x1F', '\x22', '\x3C', '\x3E', '\x7C', ':', '*', '?', '\\', '/'
|
||||
};
|
||||
|
||||
foreach (char c in invalidCharacters)
|
||||
entry = entry.Replace(c.ToString(), string.Empty);
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,15 @@ namespace osu.Game.Database
|
||||
: getReaderFrom(Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the file that is encapsulated by this <see cref="ImportTask"/>.
|
||||
/// </summary>
|
||||
public virtual void DeleteFile()
|
||||
{
|
||||
if (File.Exists(Path))
|
||||
File.Delete(Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ArchiveReader"/> from a stream.
|
||||
/// </summary>
|
||||
|
@ -201,8 +201,8 @@ namespace osu.Game.Database
|
||||
// TODO: Add a check to prevent files from storage to be deleted.
|
||||
try
|
||||
{
|
||||
if (import != null && File.Exists(task.Path) && ShouldDeleteArchive(task.Path))
|
||||
File.Delete(task.Path);
|
||||
if (import != null && ShouldDeleteArchive(task.Path))
|
||||
task.DeleteFile();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -294,8 +294,14 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
vertexBatch = renderer.CreateQuadBatch<TexturedVertex2D>(Source.AimCount, 1);
|
||||
}
|
||||
|
||||
// Due to triangles having various sizes we would need to set a different "texelSize" value for each of them, which is insanely expensive, thus we should use one single value.
|
||||
// texelSize computed for an average triangle (size 100) will result in big triangles becoming blurry, so we may just use 0 for all of them.
|
||||
// But we still need to specify at least something, because otherwise other shader usages will override this value.
|
||||
float texelSize = 0f;
|
||||
|
||||
shader.Bind();
|
||||
shader.GetUniform<float>("thickness").UpdateValue(ref fill);
|
||||
shader.GetUniform<float>("texelSize").UpdateValue(ref texelSize);
|
||||
|
||||
foreach (TriangleParticle particle in parts)
|
||||
{
|
||||
|
@ -34,6 +34,12 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
/// </summary>
|
||||
protected virtual bool CreateNewTriangles => true;
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, only the portion of triangles that falls within this <see cref="Drawable"/>'s
|
||||
/// shape is drawn to the screen.
|
||||
/// </summary>
|
||||
public bool Masking { get; set; }
|
||||
|
||||
private readonly BindableFloat spawnRatio = new BindableFloat(1f);
|
||||
|
||||
/// <summary>
|
||||
@ -189,6 +195,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
private Vector2 size;
|
||||
private float thickness;
|
||||
private float texelSize;
|
||||
private bool masking;
|
||||
|
||||
private IVertexBatch<TexturedVertex2D>? vertexBatch;
|
||||
|
||||
@ -205,6 +212,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
texture = Source.texture;
|
||||
size = Source.DrawSize;
|
||||
thickness = Source.Thickness;
|
||||
masking = Source.Masking;
|
||||
|
||||
Quad triangleQuad = new Quad(
|
||||
Vector2Extensions.Transform(Vector2.Zero, DrawInfo.Matrix),
|
||||
@ -236,26 +244,31 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
shader.GetUniform<float>("thickness").UpdateValue(ref thickness);
|
||||
shader.GetUniform<float>("texelSize").UpdateValue(ref texelSize);
|
||||
|
||||
float relativeHeight = triangleSize.Y / size.Y;
|
||||
float relativeWidth = triangleSize.X / size.X;
|
||||
Vector2 relativeSize = Vector2.Divide(triangleSize, size);
|
||||
|
||||
foreach (TriangleParticle particle in parts)
|
||||
{
|
||||
Vector2 topLeft = particle.Position - new Vector2(relativeWidth * 0.5f, 0f);
|
||||
Vector2 topRight = topLeft + new Vector2(relativeWidth, 0f);
|
||||
Vector2 bottomLeft = topLeft + new Vector2(0f, relativeHeight);
|
||||
Vector2 bottomRight = bottomLeft + new Vector2(relativeWidth, 0f);
|
||||
Vector2 topLeft = particle.Position - new Vector2(relativeSize.X * 0.5f, 0f);
|
||||
|
||||
Quad triangleQuad = masking ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y);
|
||||
|
||||
var drawQuad = new Quad(
|
||||
Vector2Extensions.Transform(topLeft * size, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(topRight * size, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(bottomLeft * size, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(bottomRight * size, DrawInfo.Matrix)
|
||||
Vector2Extensions.Transform(triangleQuad.TopLeft * size, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(triangleQuad.TopRight * size, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(triangleQuad.BottomLeft * size, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(triangleQuad.BottomRight * size, DrawInfo.Matrix)
|
||||
);
|
||||
|
||||
ColourInfo colourInfo = triangleColourInfo(DrawColourInfo.Colour, new Quad(topLeft, topRight, bottomLeft, bottomRight));
|
||||
ColourInfo colourInfo = triangleColourInfo(DrawColourInfo.Colour, triangleQuad);
|
||||
|
||||
renderer.DrawQuad(texture, drawQuad, colourInfo, vertexAction: vertexBatch.AddAction);
|
||||
RectangleF textureCoords = new RectangleF(
|
||||
triangleQuad.TopLeft.X - topLeft.X,
|
||||
triangleQuad.TopLeft.Y - topLeft.Y,
|
||||
triangleQuad.Width,
|
||||
triangleQuad.Height
|
||||
) / relativeSize;
|
||||
|
||||
renderer.DrawQuad(texture, drawQuad, colourInfo, new RectangleF(0, 0, 1, 1), vertexBatch.AddAction, textureCoords: textureCoords);
|
||||
}
|
||||
|
||||
shader.Unbind();
|
||||
@ -272,6 +285,19 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
};
|
||||
}
|
||||
|
||||
private static Quad clampToDrawable(Vector2 topLeft, Vector2 size)
|
||||
{
|
||||
float leftClamped = Math.Clamp(topLeft.X, 0f, 1f);
|
||||
float topClamped = Math.Clamp(topLeft.Y, 0f, 1f);
|
||||
|
||||
return new Quad(
|
||||
leftClamped,
|
||||
topClamped,
|
||||
Math.Clamp(topLeft.X + size.X, 0f, 1f) - leftClamped,
|
||||
Math.Clamp(topLeft.Y + size.Y, 0f, 1f) - topClamped
|
||||
);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
@ -42,8 +42,12 @@ namespace osu.Game.Graphics.Containers.Markdown
|
||||
|
||||
protected override void AddFootnoteBacklink(FootnoteLink footnoteBacklink) => AddDrawable(new OsuMarkdownFootnoteBacklink(footnoteBacklink));
|
||||
|
||||
protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic)
|
||||
=> CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic));
|
||||
protected override void ApplyEmphasisedCreationParameters(SpriteText spriteText, bool bold, bool italic)
|
||||
{
|
||||
base.ApplyEmphasisedCreationParameters(spriteText, bold, italic);
|
||||
|
||||
spriteText.Font = spriteText.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic);
|
||||
}
|
||||
|
||||
protected override void AddCustomComponent(CustomContainerInline inline)
|
||||
{
|
||||
|
@ -44,8 +44,11 @@ namespace osu.Game.Graphics.Containers
|
||||
content.AutoSizeAxes = AutoSizeAxes;
|
||||
}
|
||||
|
||||
AddInternal(content);
|
||||
Add(CreateHoverSounds(sampleSet));
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
content,
|
||||
CreateHoverSounds(sampleSet)
|
||||
});
|
||||
}
|
||||
|
||||
protected override void ClearInternal(bool disposeChildren = true) =>
|
||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
if (buttons.Contains(e.Button) && Contains(e.ScreenSpaceMousePosition))
|
||||
if (buttons.Contains(e.Button))
|
||||
{
|
||||
var channel = Enabled.Value ? sampleClick?.GetChannel() : sampleClickDisabled?.GetChannel();
|
||||
|
||||
|
@ -5,19 +5,22 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Configuration;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles debouncing hover sounds at a global level to ensure the effects are not overwhelming.
|
||||
/// </summary>
|
||||
public abstract partial class HoverSampleDebounceComponent : CompositeDrawable
|
||||
public abstract partial class HoverSampleDebounceComponent : Component
|
||||
{
|
||||
private Bindable<double?> lastPlaybackTime;
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.ReceivePositionalInputAt(screenSpacePos) == true;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(SessionStatics statics)
|
||||
{
|
||||
|
@ -116,7 +116,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
});
|
||||
|
||||
if (hoverSounds.HasValue)
|
||||
Add(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
|
||||
AddInternal(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -35,6 +35,7 @@ namespace osu.Game.Input.Bindings
|
||||
// It is used to decide the order of precedence, with the earlier items having higher precedence.
|
||||
public override IEnumerable<IKeyBinding> DefaultKeyBindings => GlobalKeyBindings
|
||||
.Concat(EditorKeyBindings)
|
||||
.Concat(ReplayKeyBindings)
|
||||
.Concat(InGameKeyBindings)
|
||||
.Concat(SongSelectKeyBindings)
|
||||
.Concat(AudioControlKeyBindings)
|
||||
@ -112,13 +113,18 @@ namespace osu.Game.Input.Bindings
|
||||
new KeyBinding(new[] { InputKey.F4 }, GlobalAction.IncreaseScrollSpeed),
|
||||
new KeyBinding(new[] { InputKey.Shift, InputKey.Tab }, GlobalAction.ToggleInGameInterface),
|
||||
new KeyBinding(InputKey.MouseMiddle, GlobalAction.PauseGameplay),
|
||||
new KeyBinding(InputKey.Space, GlobalAction.TogglePauseReplay),
|
||||
new KeyBinding(InputKey.Left, GlobalAction.SeekReplayBackward),
|
||||
new KeyBinding(InputKey.Right, GlobalAction.SeekReplayForward),
|
||||
new KeyBinding(InputKey.Control, GlobalAction.HoldForHUD),
|
||||
new KeyBinding(InputKey.Tab, GlobalAction.ToggleChatFocus),
|
||||
};
|
||||
|
||||
public IEnumerable<KeyBinding> ReplayKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.Space, GlobalAction.TogglePauseReplay),
|
||||
new KeyBinding(InputKey.MouseMiddle, GlobalAction.TogglePauseReplay),
|
||||
new KeyBinding(InputKey.Left, GlobalAction.SeekReplayBackward),
|
||||
new KeyBinding(InputKey.Right, GlobalAction.SeekReplayForward),
|
||||
};
|
||||
|
||||
public IEnumerable<KeyBinding> SongSelectKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.F1, GlobalAction.ToggleModSelection),
|
||||
|
89
osu.Game/Localisation/HUD/BarHitErrorMeterStrings.cs
Normal file
89
osu.Game/Localisation/HUD/BarHitErrorMeterStrings.cs
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation.HUD
|
||||
{
|
||||
public static class BarHitErrorMeterStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.HUD.BarHitErrorMeter";
|
||||
|
||||
/// <summary>
|
||||
/// "Judgement line thickness"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementLineThickness => new TranslatableString(getKey(@"judgement_line_thickness"), "Judgement line thickness");
|
||||
|
||||
/// <summary>
|
||||
/// "How thick the individual lines should be."
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementLineThicknessDescription => new TranslatableString(getKey(@"judgement_line_thickness_description"), "How thick the individual lines should be.");
|
||||
|
||||
/// <summary>
|
||||
/// "Show colour bars"
|
||||
/// </summary>
|
||||
public static LocalisableString ColourBarVisibility => new TranslatableString(getKey(@"colour_bar_visibility"), "Show colour bars");
|
||||
|
||||
/// <summary>
|
||||
/// "Show moving average arrow"
|
||||
/// </summary>
|
||||
public static LocalisableString ShowMovingAverage => new TranslatableString(getKey(@"show_moving_average"), "Show moving average arrow");
|
||||
|
||||
/// <summary>
|
||||
/// "Whether an arrow should move beneath the bar showing the average error."
|
||||
/// </summary>
|
||||
public static LocalisableString ShowMovingAverageDescription => new TranslatableString(getKey(@"show_moving_average_description"), "Whether an arrow should move beneath the bar showing the average error.");
|
||||
|
||||
/// <summary>
|
||||
/// "Centre marker style"
|
||||
/// </summary>
|
||||
public static LocalisableString CentreMarkerStyle => new TranslatableString(getKey(@"centre_marker_style"), "Centre marker style");
|
||||
|
||||
/// <summary>
|
||||
/// "How to signify the centre of the display"
|
||||
/// </summary>
|
||||
public static LocalisableString CentreMarkerStyleDescription => new TranslatableString(getKey(@"centre_marker_style_description"), "How to signify the centre of the display");
|
||||
|
||||
/// <summary>
|
||||
/// "None"
|
||||
/// </summary>
|
||||
public static LocalisableString CentreMarkerStylesNone => new TranslatableString(getKey(@"centre_marker_styles_none"), "None");
|
||||
|
||||
/// <summary>
|
||||
/// "Circle"
|
||||
/// </summary>
|
||||
public static LocalisableString CentreMarkerStylesCircle => new TranslatableString(getKey(@"centre_marker_styles_circle"), "Circle");
|
||||
|
||||
/// <summary>
|
||||
/// "Line"
|
||||
/// </summary>
|
||||
public static LocalisableString CentreMarkerStylesLine => new TranslatableString(getKey(@"centre_marker_styles_line"), "Line");
|
||||
|
||||
/// <summary>
|
||||
/// "Label style"
|
||||
/// </summary>
|
||||
public static LocalisableString LabelStyle => new TranslatableString(getKey(@"label_style"), "Label style");
|
||||
|
||||
/// <summary>
|
||||
/// "How to show early/late extremities"
|
||||
/// </summary>
|
||||
public static LocalisableString LabelStyleDescription => new TranslatableString(getKey(@"label_style_description"), "How to show early/late extremities");
|
||||
|
||||
/// <summary>
|
||||
/// "None"
|
||||
/// </summary>
|
||||
public static LocalisableString LabelStylesNone => new TranslatableString(getKey(@"label_styles_none"), "None");
|
||||
|
||||
/// <summary>
|
||||
/// "Icons"
|
||||
/// </summary>
|
||||
public static LocalisableString LabelStylesIcons => new TranslatableString(getKey(@"label_styles_icons"), "Icons");
|
||||
|
||||
/// <summary>
|
||||
/// "Text"
|
||||
/// </summary>
|
||||
public static LocalisableString LabelStylesText => new TranslatableString(getKey(@"label_styles_text"), "Text");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
54
osu.Game/Localisation/HUD/ColourHitErrorMeterStrings.cs
Normal file
54
osu.Game/Localisation/HUD/ColourHitErrorMeterStrings.cs
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation.HUD
|
||||
{
|
||||
public static class ColourHitErrorMeterStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.HUD.ColourHitError";
|
||||
|
||||
/// <summary>
|
||||
/// "Judgement count"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementCount => new TranslatableString(getKey(@"judgement_count"), "Judgement count");
|
||||
|
||||
/// <summary>
|
||||
/// "The number of displayed judgements"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementCountDescription => new TranslatableString(getKey(@"judgement_count_description"), "The number of displayed judgements");
|
||||
|
||||
/// <summary>
|
||||
/// "Judgement spacing"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementSpacing => new TranslatableString(getKey(@"judgement_spacing"), "Judgement spacing");
|
||||
|
||||
/// <summary>
|
||||
/// "The space between each displayed judgement"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementSpacingDescription => new TranslatableString(getKey(@"judgement_spacing_description"), "The space between each displayed judgement");
|
||||
|
||||
/// <summary>
|
||||
/// "Judgement shape"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementShape => new TranslatableString(getKey(@"judgement_shape"), "Judgement shape");
|
||||
|
||||
/// <summary>
|
||||
/// "The shape of each displayed judgement"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementShapeDescription => new TranslatableString(getKey(@"judgement_shape_description"), "The shape of each displayed judgement");
|
||||
|
||||
/// <summary>
|
||||
/// "Circle"
|
||||
/// </summary>
|
||||
public static LocalisableString ShapeStyleCircle => new TranslatableString(getKey(@"shape_style_cricle"), "Circle");
|
||||
|
||||
/// <summary>
|
||||
/// "Square"
|
||||
/// </summary>
|
||||
public static LocalisableString ShapeStyleSquare => new TranslatableString(getKey(@"shape_style_square"), "Square");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
39
osu.Game/Localisation/HUD/GameplayAccuracyCounterStrings.cs
Normal file
39
osu.Game/Localisation/HUD/GameplayAccuracyCounterStrings.cs
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation.HUD
|
||||
{
|
||||
public static class GameplayAccuracyCounterStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.HUD.GameplayAccuracyCounter";
|
||||
|
||||
/// <summary>
|
||||
/// "Accuracy display mode"
|
||||
/// </summary>
|
||||
public static LocalisableString AccuracyDisplay => new TranslatableString(getKey(@"accuracy_display"), "Accuracy display mode");
|
||||
|
||||
/// <summary>
|
||||
/// "Which accuracy mode should be displayed."
|
||||
/// </summary>
|
||||
public static LocalisableString AccuracyDisplayDescription => new TranslatableString(getKey(@"accuracy_display_description"), "Which accuracy mode should be displayed.");
|
||||
|
||||
/// <summary>
|
||||
/// "Standard"
|
||||
/// </summary>
|
||||
public static LocalisableString AccuracyDisplayModeStandard => new TranslatableString(getKey(@"accuracy_display_mode_standard"), "Standard");
|
||||
|
||||
/// <summary>
|
||||
/// "Maximum achievable"
|
||||
/// </summary>
|
||||
public static LocalisableString AccuracyDisplayModeMax => new TranslatableString(getKey(@"accuracy_display_mode_max"), "Maximum achievable");
|
||||
|
||||
/// <summary>
|
||||
/// "Minimum achievable"
|
||||
/// </summary>
|
||||
public static LocalisableString AccuracyDisplayModeMin => new TranslatableString(getKey(@"accuracy_display_mode_min"), "Minimum achievable");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
49
osu.Game/Localisation/HUD/JudgementCounterDisplayStrings.cs
Normal file
49
osu.Game/Localisation/HUD/JudgementCounterDisplayStrings.cs
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation.HUD
|
||||
{
|
||||
public static class JudgementCounterDisplayStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.HUD.JudgementCounterDisplay";
|
||||
|
||||
/// <summary>
|
||||
/// "Display mode"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementDisplayMode => new TranslatableString(getKey(@"judgement_display_mode"), "Display mode");
|
||||
|
||||
/// <summary>
|
||||
/// "Counter direction"
|
||||
/// </summary>
|
||||
public static LocalisableString FlowDirection => new TranslatableString(getKey(@"flow_direction"), "Counter direction");
|
||||
|
||||
/// <summary>
|
||||
/// "Show judgement names"
|
||||
/// </summary>
|
||||
public static LocalisableString ShowJudgementNames => new TranslatableString(getKey(@"show_judgement_names"), "Show judgement names");
|
||||
|
||||
/// <summary>
|
||||
/// "Show max judgement"
|
||||
/// </summary>
|
||||
public static LocalisableString ShowMaxJudgement => new TranslatableString(getKey(@"show_max_judgement"), "Show max judgement");
|
||||
|
||||
/// <summary>
|
||||
/// "Simple"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementDisplayModeSimple => new TranslatableString(getKey(@"judgement_display_mode_simple"), "Simple");
|
||||
|
||||
/// <summary>
|
||||
/// "Normal"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementDisplayModeNormal => new TranslatableString(getKey(@"judgement_display_mode_normal"), "Normal");
|
||||
|
||||
/// <summary>
|
||||
/// "All"
|
||||
/// </summary>
|
||||
public static LocalisableString JudgementDisplayModeAll => new TranslatableString(getKey(@"judgement_display_mode_all"), "All");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
24
osu.Game/Localisation/HUD/SongProgressStrings.cs
Normal file
24
osu.Game/Localisation/HUD/SongProgressStrings.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation.HUD
|
||||
{
|
||||
public static class SongProgressStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.HUD.SongProgress";
|
||||
|
||||
/// <summary>
|
||||
/// "Show difficulty graph"
|
||||
/// </summary>
|
||||
public static LocalisableString ShowGraph => new TranslatableString(getKey(@"show_graph"), "Show difficulty graph");
|
||||
|
||||
/// <summary>
|
||||
/// "Whether a graph displaying difficulty throughout the beatmap should be shown"
|
||||
/// </summary>
|
||||
public static LocalisableString ShowGraphDescription => new TranslatableString(getKey(@"show_graph_description"), "Whether a graph displaying difficulty throughout the beatmap should be shown");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
@ -34,6 +34,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString InGameSection => new TranslatableString(getKey(@"in_game_section"), @"In Game");
|
||||
|
||||
/// <summary>
|
||||
/// "Replay"
|
||||
/// </summary>
|
||||
public static LocalisableString ReplaySection => new TranslatableString(getKey(@"replay_section"), @"Replay");
|
||||
|
||||
/// <summary>
|
||||
/// "Audio"
|
||||
/// </summary>
|
||||
|
@ -0,0 +1,34 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation.SkinComponents
|
||||
{
|
||||
public static class BeatmapAttributeTextStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.SkinComponents.BeatmapAttributeText";
|
||||
|
||||
/// <summary>
|
||||
/// "Attribute"
|
||||
/// </summary>
|
||||
public static LocalisableString Attribute => new TranslatableString(getKey(@"attribute"), "Attribute");
|
||||
|
||||
/// <summary>
|
||||
/// "The attribute to be displayed."
|
||||
/// </summary>
|
||||
public static LocalisableString AttributeDescription => new TranslatableString(getKey(@"attribute_description"), "The attribute to be displayed.");
|
||||
|
||||
/// <summary>
|
||||
/// "Template"
|
||||
/// </summary>
|
||||
public static LocalisableString Template => new TranslatableString(getKey(@"template"), "Template");
|
||||
|
||||
/// <summary>
|
||||
/// "Supports {{Label}} and {{Value}}, but also including arbitrary attributes like {{StarRating}} (see attribute list for supported values)."
|
||||
/// </summary>
|
||||
public static LocalisableString TemplateDescription => new TranslatableString(getKey(@"template_description"), @"Supports {{Label}} and {{Value}}, but also including arbitrary attributes like {{StarRating}} (see attribute list for supported values).");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation.SkinComponents
|
||||
{
|
||||
public static class SkinnableComponentStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.SkinComponents.SkinnableComponentStrings";
|
||||
|
||||
/// <summary>
|
||||
/// "Sprite name"
|
||||
/// </summary>
|
||||
public static LocalisableString SpriteName => new TranslatableString(getKey(@"sprite_name"), "Sprite name");
|
||||
|
||||
/// <summary>
|
||||
/// "The filename of the sprite"
|
||||
/// </summary>
|
||||
public static LocalisableString SpriteNameDescription => new TranslatableString(getKey(@"sprite_name_description"), "The filename of the sprite");
|
||||
|
||||
/// <summary>
|
||||
/// "Font"
|
||||
/// </summary>
|
||||
public static LocalisableString Font => new TranslatableString(getKey(@"font"), "Font");
|
||||
|
||||
/// <summary>
|
||||
/// "The font to use."
|
||||
/// </summary>
|
||||
public static LocalisableString FontDescription => new TranslatableString(getKey(@"font_description"), "The font to use.");
|
||||
|
||||
/// <summary>
|
||||
/// "Text"
|
||||
/// </summary>
|
||||
public static LocalisableString TextElementText => new TranslatableString(getKey(@"text_element_text"), "Text");
|
||||
|
||||
/// <summary>
|
||||
/// "The text to be displayed."
|
||||
/// </summary>
|
||||
public static LocalisableString TextElementTextDescription => new TranslatableString(getKey(@"text_element_text_description"), "The text to be displayed.");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
@ -39,6 +39,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString Settings(string arg0) => new TranslatableString(getKey(@"settings"), @"Settings ({0})", arg0);
|
||||
|
||||
/// <summary>
|
||||
/// "Currently editing"
|
||||
/// </summary>
|
||||
public static LocalisableString CurrentlyEditing => new TranslatableString(getKey(@"currently_editing"), "Currently editing");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osu.Game.Overlays.Music;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Overlays.Toolbar;
|
||||
using osu.Game.Overlays.Volume;
|
||||
using osu.Game.Performance;
|
||||
@ -59,7 +60,6 @@ using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Skinning.Editor;
|
||||
using osu.Game.Updater;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Utils;
|
||||
|
@ -17,18 +17,21 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>(Overlays.SortDirection.Descending);
|
||||
|
||||
private SearchCategory? lastCategory;
|
||||
private bool? lastHasQuery;
|
||||
private (SearchCategory category, bool hasQuery)? currentParameters;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Reset(SearchCategory.Leaderboard, false);
|
||||
|
||||
if (currentParameters == null)
|
||||
Reset(SearchCategory.Leaderboard, false);
|
||||
}
|
||||
|
||||
public void Reset(SearchCategory category, bool hasQuery)
|
||||
{
|
||||
if (category != lastCategory || hasQuery != lastHasQuery)
|
||||
var newParameters = (category, hasQuery);
|
||||
|
||||
if (currentParameters != newParameters)
|
||||
{
|
||||
TabControl.Clear();
|
||||
|
||||
@ -63,8 +66,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
// see: https://github.com/ppy/osu-framework/issues/5412
|
||||
TabControl.Current.TriggerChange();
|
||||
|
||||
lastCategory = category;
|
||||
lastHasQuery = hasQuery;
|
||||
currentParameters = newParameters;
|
||||
}
|
||||
|
||||
protected override SortTabControl CreateControl() => new BeatmapSortTabControl
|
||||
|
@ -14,7 +14,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header
|
||||
{
|
||||
public partial class MedalHeaderContainer : CompositeDrawable
|
||||
public partial class BadgeHeaderContainer : CompositeDrawable
|
||||
{
|
||||
private FillFlowContainer badgeFlowContainer = null!;
|
||||
|
@ -35,6 +35,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
CornerRadius = 8;
|
||||
|
||||
TooltipText = group.Name;
|
||||
|
||||
if (group.IsProbationary)
|
||||
{
|
||||
Alpha = 0.6f;
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -47,7 +52,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider?.Background6 ?? Colour4.Black
|
||||
Colour = colourProvider?.Background6 ?? Colour4.Black,
|
||||
// Normal badges background opacity is 75%, probationary is full opacity as the whole badge gets a bit transparent
|
||||
// Goal is to match osu-web so this is the most accurate it can be, its a bit scuffed but it is what it is
|
||||
// Source: https://github.com/ppy/osu-web/blob/master/resources/css/bem/user-group-badge.less#L50
|
||||
Alpha = group.IsProbationary ? 1 : 0.75f,
|
||||
},
|
||||
innerContainer = new FillFlowContainer
|
||||
{
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Profile
|
||||
RelativeSizeAxes = Axes.X,
|
||||
User = { BindTarget = User },
|
||||
},
|
||||
new MedalHeaderContainer
|
||||
new BadgeHeaderContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
User = { BindTarget = User },
|
||||
|
@ -36,21 +36,24 @@ namespace osu.Game.Overlays.Profile
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = 10;
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0, 1),
|
||||
Radius = 3,
|
||||
Colour = Colour4.Black.Opacity(0.25f)
|
||||
};
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = 10,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0, 1),
|
||||
Radius = 3,
|
||||
Colour = Colour4.Black.Opacity(0.25f)
|
||||
},
|
||||
Child = background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
|
@ -25,6 +25,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
Add(new AudioControlKeyBindingsSubsection(manager));
|
||||
Add(new SongSelectKeyBindingSubsection(manager));
|
||||
Add(new InGameKeyBindingsSubsection(manager));
|
||||
Add(new ReplayKeyBindingsSubsection(manager));
|
||||
Add(new EditorKeyBindingsSubsection(manager));
|
||||
}
|
||||
|
||||
@ -72,6 +73,17 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
}
|
||||
}
|
||||
|
||||
private partial class ReplayKeyBindingsSubsection : KeyBindingsSubsection
|
||||
{
|
||||
protected override LocalisableString Header => InputSettingsStrings.ReplaySection;
|
||||
|
||||
public ReplayKeyBindingsSubsection(GlobalActionContainer manager)
|
||||
: base(null)
|
||||
{
|
||||
Defaults = manager.ReplayKeyBindings;
|
||||
}
|
||||
}
|
||||
|
||||
private partial class AudioControlKeyBindingsSubsection : KeyBindingsSubsection
|
||||
{
|
||||
protected override LocalisableString Header => InputSettingsStrings.AudioSection;
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
var directoryInfos = target.GetDirectories();
|
||||
var fileInfos = target.GetFiles();
|
||||
|
||||
if (directoryInfos.Length > 0 || fileInfos.Length > 0)
|
||||
if (directoryInfos.Length > 0 || fileInfos.Length > 0 || target.Parent == null)
|
||||
{
|
||||
// Quick test for whether there's already an osu! install at the target path.
|
||||
if (fileInfos.Any(f => f.Name == OsuGameBase.CLIENT_DATABASE_FILENAME))
|
||||
@ -65,7 +65,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
return;
|
||||
}
|
||||
|
||||
target = target.CreateSubdirectory("osu-lazer");
|
||||
// Not using CreateSubDirectory as it throws unexpectedly when attempting to create a directory when already at the root of a disk.
|
||||
// See https://cs.github.com/dotnet/runtime/blob/f1bdd5a6182f43f3928b389b03f7bc26f826c8bc/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryInfo.cs#L88-L94
|
||||
target = Directory.CreateDirectory(Path.Combine(target.FullName, @"osu-lazer"));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -17,9 +17,9 @@ using osu.Framework.Platform;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Skinning.Editor;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
@ -13,25 +11,26 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Skinning.Editor
|
||||
namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
public partial class SkinBlueprint : SelectionBlueprint<ISkinnableDrawable>
|
||||
{
|
||||
private Container box;
|
||||
private Container box = null!;
|
||||
|
||||
private Container outlineBox;
|
||||
private Container outlineBox = null!;
|
||||
|
||||
private AnchorOriginVisualiser anchorOriginVisualiser;
|
||||
private AnchorOriginVisualiser anchorOriginVisualiser = null!;
|
||||
|
||||
private Drawable drawable => (Drawable)Item;
|
||||
|
||||
protected override bool ShouldBeAlive => drawable.IsAlive && Item.IsPresent;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public SkinBlueprint(ISkinnableDrawable component)
|
||||
: base(component)
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
@ -10,16 +8,21 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Skinning.Editor
|
||||
namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
public partial class SkinBlueprintContainer : BlueprintContainer<ISkinnableDrawable>
|
||||
{
|
||||
@ -28,7 +31,7 @@ namespace osu.Game.Skinning.Editor
|
||||
private readonly List<BindableList<ISkinnableDrawable>> targetComponents = new List<BindableList<ISkinnableDrawable>>();
|
||||
|
||||
[Resolved]
|
||||
private SkinEditor editor { get; set; }
|
||||
private SkinEditor editor { get; set; } = null!;
|
||||
|
||||
public SkinBlueprintContainer(Drawable target)
|
||||
{
|
||||
@ -46,9 +49,7 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
if (targetContainers.Length == 0)
|
||||
{
|
||||
string targetScreen = target.ChildrenOfType<Screen>().LastOrDefault()?.GetType().Name ?? "this screen";
|
||||
|
||||
AddInternal(new ScreenWhiteBox.UnderConstructionMessage(targetScreen, "doesn't support skin customisation just yet."));
|
||||
AddInternal(new NonSkinnableScreenPlaceholder());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -61,7 +62,7 @@ namespace osu.Game.Skinning.Editor
|
||||
}
|
||||
}
|
||||
|
||||
private void componentsChanged(object sender, NotifyCollectionChangedEventArgs e) => Schedule(() =>
|
||||
private void componentsChanged(object? sender, NotifyCollectionChangedEventArgs e) => Schedule(() =>
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
@ -159,5 +160,65 @@ namespace osu.Game.Skinning.Editor
|
||||
foreach (var list in targetComponents)
|
||||
list.UnbindAll();
|
||||
}
|
||||
|
||||
public partial class NonSkinnableScreenPlaceholder : CompositeDrawable
|
||||
{
|
||||
[Resolved]
|
||||
private SkinEditorOverlay? skinEditorOverlay { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colourProvider.Dark6,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.95f,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0, 5),
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Icon = FontAwesome.Solid.ExclamationCircle,
|
||||
Size = new Vector2(24),
|
||||
Y = -5,
|
||||
},
|
||||
new OsuTextFlowContainer(t => t.Font = OsuFont.Default.With(weight: FontWeight.SemiBold, size: 18))
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
TextAnchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = "Please navigate to a skinnable screen using the scene library",
|
||||
},
|
||||
new RoundedButton
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Width = 200,
|
||||
Margin = new MarginPadding { Top = 20 },
|
||||
Action = () => skinEditorOverlay?.Hide(),
|
||||
Text = "Return to game"
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,12 +11,12 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Skinning.Editor
|
||||
namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
public partial class SkinComponentToolbox : EditorSidebarSection
|
||||
{
|
||||
@ -40,7 +40,7 @@ namespace osu.Game.Skinning.Editor
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(2)
|
||||
Spacing = new Vector2(EditorSidebar.PADDING)
|
||||
};
|
||||
|
||||
reloadComponents();
|
@ -1,10 +1,9 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -24,17 +23,17 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.OSD;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osu.Game.Screens.Edit.Components.Menus;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Skinning.Editor
|
||||
namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
[Cached(typeof(SkinEditor))]
|
||||
public partial class SkinEditor : VisibilityContainer, ICanAcceptFiles, IKeyBindingHandler<PlatformAction>
|
||||
{
|
||||
public const double TRANSITION_DURATION = 500;
|
||||
public const double TRANSITION_DURATION = 300;
|
||||
|
||||
public const float MENU_HEIGHT = 40;
|
||||
|
||||
@ -42,39 +41,39 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
protected override bool StartHidden => true;
|
||||
|
||||
private Drawable targetScreen;
|
||||
private Drawable targetScreen = null!;
|
||||
|
||||
private OsuTextFlowContainer headerText;
|
||||
private OsuTextFlowContainer headerText = null!;
|
||||
|
||||
private Bindable<Skin> currentSkin;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OsuGame game { get; set; }
|
||||
private Bindable<Skin> currentSkin = null!;
|
||||
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
private OsuGame? game { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
private SkinManager skins { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; }
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private SkinEditorOverlay skinEditorOverlay { get; set; }
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private SkinEditorOverlay? skinEditorOverlay { get; set; }
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
private bool hasBegunMutating;
|
||||
|
||||
private Container content;
|
||||
private Container? content;
|
||||
|
||||
private EditorSidebar componentsSidebar;
|
||||
private EditorSidebar settingsSidebar;
|
||||
private EditorSidebar componentsSidebar = null!;
|
||||
private EditorSidebar settingsSidebar = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OnScreenDisplay onScreenDisplay { get; set; }
|
||||
[Resolved]
|
||||
private OnScreenDisplay? onScreenDisplay { get; set; }
|
||||
|
||||
public SkinEditor()
|
||||
{
|
||||
@ -126,7 +125,7 @@ namespace osu.Game.Skinning.Editor
|
||||
{
|
||||
Items = new[]
|
||||
{
|
||||
new EditorMenuItem(Resources.Localisation.Web.CommonStrings.ButtonsSave, MenuItemType.Standard, Save),
|
||||
new EditorMenuItem(Resources.Localisation.Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()),
|
||||
new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, revert),
|
||||
new EditorMenuItemSpacer(),
|
||||
new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()),
|
||||
@ -234,11 +233,14 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
// Immediately clear the previous blueprint container to ensure it doesn't try to interact with the old target.
|
||||
content?.Clear();
|
||||
|
||||
Scheduler.AddOnce(loadBlueprintContainer);
|
||||
Scheduler.AddOnce(populateSettings);
|
||||
|
||||
void loadBlueprintContainer()
|
||||
{
|
||||
Debug.Assert(content != null);
|
||||
|
||||
content.Child = new SkinBlueprintContainer(targetScreen);
|
||||
|
||||
componentsSidebar.Child = new SkinComponentToolbox(getFirstTarget() as CompositeDrawable)
|
||||
@ -254,13 +256,13 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
headerText.AddParagraph(SkinEditorStrings.SkinEditor, cp => cp.Font = OsuFont.Default.With(size: 16));
|
||||
headerText.NewParagraph();
|
||||
headerText.AddText("Currently editing ", cp =>
|
||||
headerText.AddText(SkinEditorStrings.CurrentlyEditing, cp =>
|
||||
{
|
||||
cp.Font = OsuFont.Default.With(size: 12);
|
||||
cp.Colour = colours.Yellow;
|
||||
});
|
||||
|
||||
headerText.AddText($"{currentSkin.Value.SkinInfo}", cp =>
|
||||
headerText.AddText($" {currentSkin.Value.SkinInfo}", cp =>
|
||||
{
|
||||
cp.Font = OsuFont.Default.With(size: 12, weight: FontWeight.Bold);
|
||||
cp.Colour = colours.Yellow;
|
||||
@ -311,9 +313,9 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
private IEnumerable<ISkinnableTarget> availableTargets => targetScreen.ChildrenOfType<ISkinnableTarget>();
|
||||
|
||||
private ISkinnableTarget getFirstTarget() => availableTargets.FirstOrDefault();
|
||||
private ISkinnableTarget? getFirstTarget() => availableTargets.FirstOrDefault();
|
||||
|
||||
private ISkinnableTarget getTarget(GlobalSkinComponentLookup.LookupType target)
|
||||
private ISkinnableTarget? getTarget(GlobalSkinComponentLookup.LookupType target)
|
||||
{
|
||||
return availableTargets.FirstOrDefault(c => c.Target == target);
|
||||
}
|
||||
@ -327,11 +329,11 @@ namespace osu.Game.Skinning.Editor
|
||||
currentSkin.Value.ResetDrawableTarget(t);
|
||||
|
||||
// add back default components
|
||||
getTarget(t.Target).Reload();
|
||||
getTarget(t.Target)?.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
public void Save(bool userTriggered = true)
|
||||
{
|
||||
if (!hasBegunMutating)
|
||||
return;
|
||||
@ -341,8 +343,9 @@ namespace osu.Game.Skinning.Editor
|
||||
foreach (var t in targetContainers)
|
||||
currentSkin.Value.UpdateDrawableTarget(t);
|
||||
|
||||
skins.Save(skins.CurrentSkin.Value);
|
||||
onScreenDisplay?.Display(new SkinEditorToast(ToastStrings.SkinSaved, currentSkin.Value.SkinInfo.ToString()));
|
||||
// In the case the save was user triggered, always show the save message to make them feel confident.
|
||||
if (skins.Save(skins.CurrentSkin.Value) || userTriggered)
|
||||
onScreenDisplay?.Display(new SkinEditorToast(ToastStrings.SkinSaved, currentSkin.Value.SkinInfo.ToString() ?? "Unknown"));
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e) => true;
|
||||
@ -357,10 +360,7 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
this
|
||||
// align animation to happen after the majority of the ScalingContainer animation completes.
|
||||
.Delay(ScalingContainer.TRANSITION_DURATION * 0.3f)
|
||||
.FadeIn(TRANSITION_DURATION, Easing.OutQuint);
|
||||
this.FadeIn(TRANSITION_DURATION, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
@ -394,12 +394,18 @@ namespace osu.Game.Skinning.Editor
|
||||
// This is the best we can do for now.
|
||||
realm.Run(r => r.Refresh());
|
||||
|
||||
var skinnableTarget = getFirstTarget();
|
||||
|
||||
// Import still should happen for now, even if not placeable (as it allows a user to import skin resources that would apply to legacy gameplay skins).
|
||||
if (skinnableTarget == null)
|
||||
return;
|
||||
|
||||
// place component
|
||||
var sprite = new SkinnableSprite
|
||||
{
|
||||
SpriteName = { Value = file.Name },
|
||||
Origin = Anchor.Centre,
|
||||
Position = getFirstTarget().ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position),
|
||||
Position = skinnableTarget.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position),
|
||||
};
|
||||
|
||||
placeComponent(sprite, false);
|
@ -1,10 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Diagnostics;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -17,7 +14,7 @@ using osu.Game.Screens;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Skinning.Editor
|
||||
namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// A container which handles loading a skin editor on user request for a specified target.
|
||||
@ -29,13 +26,12 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
protected override bool BlockNonPositionalInput => true;
|
||||
|
||||
[CanBeNull]
|
||||
private SkinEditor skinEditor;
|
||||
private SkinEditor? skinEditor;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OsuGame game { get; set; }
|
||||
[Resolved]
|
||||
private OsuGame game { get; set; } = null!;
|
||||
|
||||
private OsuScreen lastTargetScreen;
|
||||
private OsuScreen? lastTargetScreen;
|
||||
|
||||
private Vector2 lastDrawSize;
|
||||
|
||||
@ -81,6 +77,8 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
AddInternal(editor);
|
||||
|
||||
Debug.Assert(lastTargetScreen != null);
|
||||
|
||||
SetTarget(lastTargetScreen);
|
||||
});
|
||||
}
|
||||
@ -124,15 +122,15 @@ namespace osu.Game.Skinning.Editor
|
||||
{
|
||||
Scheduler.AddOnce(updateScreenSizing);
|
||||
|
||||
game?.Toolbar.Hide();
|
||||
game?.CloseAllOverlays();
|
||||
game.Toolbar.Hide();
|
||||
game.CloseAllOverlays();
|
||||
}
|
||||
else
|
||||
{
|
||||
scalingContainer.SetCustomRect(null);
|
||||
|
||||
if (lastTargetScreen?.HideOverlaysOnEnter != true)
|
||||
game?.Toolbar.Show();
|
||||
game.Toolbar.Show();
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,7 +147,7 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
if (skinEditor == null) return;
|
||||
|
||||
skinEditor.Save();
|
||||
skinEditor.Save(userTriggered: false);
|
||||
|
||||
// ensure the toolbar is re-hidden even if a new screen decides to try and show it.
|
||||
updateComponentVisibility();
|
||||
@ -158,7 +156,7 @@ namespace osu.Game.Skinning.Editor
|
||||
Scheduler.AddOnce(setTarget, screen);
|
||||
}
|
||||
|
||||
private void setTarget(OsuScreen target)
|
||||
private void setTarget(OsuScreen? target)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
@ -1,11 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -17,7 +14,6 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens;
|
||||
@ -26,7 +22,7 @@ using osu.Game.Screens.Select;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Skinning.Editor
|
||||
namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
public partial class SkinEditorSceneLibrary : CompositeDrawable
|
||||
{
|
||||
@ -36,14 +32,14 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
private const float padding = 10;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private IPerformFromScreenRunner performer { get; set; }
|
||||
[Resolved]
|
||||
private IPerformFromScreenRunner? performer { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||
private Bindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
|
||||
|
||||
public SkinEditorSceneLibrary()
|
||||
{
|
||||
@ -107,7 +103,12 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod();
|
||||
|
||||
if (!ModUtils.CheckCompatibleSet(mods.Value.Append(replayGeneratingMod), out var invalid))
|
||||
IReadOnlyList<Mod> usableMods = mods.Value;
|
||||
|
||||
if (replayGeneratingMod != null)
|
||||
usableMods = usableMods.Append(replayGeneratingMod).ToArray();
|
||||
|
||||
if (!ModUtils.CheckCompatibleSet(usableMods, out var invalid))
|
||||
mods.Value = mods.Value.Except(invalid).ToArray();
|
||||
|
||||
if (replayGeneratingMod != null)
|
||||
@ -129,8 +130,8 @@ namespace osu.Game.Skinning.Editor
|
||||
Height = BUTTON_HEIGHT;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider? overlayColourProvider, OsuColour colours)
|
||||
{
|
||||
BackgroundColour = overlayColourProvider?.Background3 ?? colours.Blue3;
|
||||
Content.CornerRadius = 5;
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -16,14 +14,15 @@ using osu.Game.Extensions;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Skinning.Editor
|
||||
namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
public partial class SkinSelectionHandler : SelectionHandler<ISkinnableDrawable>
|
||||
{
|
||||
[Resolved]
|
||||
private SkinEditor skinEditor { get; set; }
|
||||
private SkinEditor skinEditor { get; set; } = null!;
|
||||
|
||||
public override bool HandleRotation(float angle)
|
||||
{
|
@ -9,7 +9,7 @@ using osu.Game.Localisation;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Skinning.Editor
|
||||
namespace osu.Game.Overlays.SkinEditor
|
||||
{
|
||||
internal partial class SkinSettingsToolbox : EditorSidebarSection
|
||||
{
|
@ -36,6 +36,9 @@ namespace osu.Game.Rulesets.Edit
|
||||
new CheckUnsnappedObjects(),
|
||||
new CheckConcurrentObjects(),
|
||||
new CheckZeroLengthObjects(),
|
||||
|
||||
// Timing
|
||||
new CheckPreviewTime(),
|
||||
};
|
||||
|
||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||
|
62
osu.Game/Rulesets/Edit/Checks/CheckPreviewTime.cs
Normal file
62
osu.Game/Rulesets/Edit/Checks/CheckPreviewTime.cs
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit.Checks.Components;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit.Checks
|
||||
{
|
||||
public class CheckPreviewTime : ICheck
|
||||
{
|
||||
public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Timing, "Inconsistent or unset preview time");
|
||||
|
||||
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
||||
{
|
||||
new IssueTemplatePreviewTimeConflict(this),
|
||||
new IssueTemplateHasNoPreviewTime(this),
|
||||
};
|
||||
|
||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||
{
|
||||
var diffList = context.Beatmap.BeatmapInfo.BeatmapSet?.Beatmaps ?? new List<BeatmapInfo>();
|
||||
int previewTime = context.Beatmap.BeatmapInfo.Metadata.PreviewTime;
|
||||
|
||||
if (previewTime == -1)
|
||||
yield return new IssueTemplateHasNoPreviewTime(this).Create();
|
||||
|
||||
foreach (var diff in diffList)
|
||||
{
|
||||
if (diff.Equals(context.Beatmap.BeatmapInfo))
|
||||
continue;
|
||||
|
||||
if (diff.Metadata.PreviewTime != previewTime)
|
||||
yield return new IssueTemplatePreviewTimeConflict(this).Create(diff.DifficultyName, previewTime, diff.Metadata.PreviewTime);
|
||||
}
|
||||
}
|
||||
|
||||
public class IssueTemplatePreviewTimeConflict : IssueTemplate
|
||||
{
|
||||
public IssueTemplatePreviewTimeConflict(ICheck check)
|
||||
: base(check, IssueType.Problem, "Audio preview time ({1}) doesn't match the time specified in \"{0}\" ({2})")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(string diffName, int originalTime, int conflictTime) =>
|
||||
// preview time should show (not set) when it is not set.
|
||||
new Issue(this, diffName,
|
||||
originalTime != -1 ? $"{originalTime:N0} ms" : "not set",
|
||||
conflictTime != -1 ? $"{conflictTime:N0} ms" : "not set");
|
||||
}
|
||||
|
||||
public class IssueTemplateHasNoPreviewTime : IssueTemplate
|
||||
{
|
||||
public IssueTemplateHasNoPreviewTime(ICheck check)
|
||||
: base(check, IssueType.Problem, "A preview point for this map is not set. Consider setting one from the Timing menu.")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create() => new Issue(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// </summary>
|
||||
protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty);
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.ReceivePositionalInputAt(screenSpacePos) ?? false;
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.ReceivePositionalInputAt(screenSpacePos) == true;
|
||||
|
||||
protected override bool Handle(UIEvent e)
|
||||
{
|
||||
|
@ -303,8 +303,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
samplesBindable.CollectionChanged -= onSamplesChanged;
|
||||
|
||||
// Release the samples for other hitobjects to use.
|
||||
if (Samples != null)
|
||||
Samples.Samples = null;
|
||||
Samples?.ClearSamples();
|
||||
|
||||
foreach (var obj in nestedHitObjects)
|
||||
{
|
||||
|
@ -157,6 +157,8 @@ namespace osu.Game.Rulesets.UI
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void AddExtension(string extension) => throw new NotSupportedException();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (primary.IsNotNull()) primary.Dispose();
|
||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Screens.Edit.Components
|
||||
{
|
||||
public const float WIDTH = 250;
|
||||
|
||||
public const float PADDING = 3;
|
||||
|
||||
private readonly Box background;
|
||||
|
||||
protected override Container<EditorSidebarSection> Content { get; }
|
||||
@ -35,13 +37,13 @@ namespace osu.Game.Screens.Edit.Components
|
||||
},
|
||||
new OsuScrollContainer
|
||||
{
|
||||
Padding = new MarginPadding { Left = 20 },
|
||||
ScrollbarOverlapsContent = false,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = Content = new FillFlowContainer<EditorSidebarSection>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding(PADDING),
|
||||
Direction = FillDirection.Vertical,
|
||||
},
|
||||
}
|
||||
|
@ -91,6 +91,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
blueprint.DrawableObject = drawableObject;
|
||||
}
|
||||
|
||||
private bool nudgeMovementActive;
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (e.ControlPressed)
|
||||
@ -98,19 +100,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Left:
|
||||
moveSelection(new Vector2(-1, 0));
|
||||
nudgeSelection(new Vector2(-1, 0));
|
||||
return true;
|
||||
|
||||
case Key.Right:
|
||||
moveSelection(new Vector2(1, 0));
|
||||
nudgeSelection(new Vector2(1, 0));
|
||||
return true;
|
||||
|
||||
case Key.Up:
|
||||
moveSelection(new Vector2(0, -1));
|
||||
nudgeSelection(new Vector2(0, -1));
|
||||
return true;
|
||||
|
||||
case Key.Down:
|
||||
moveSelection(new Vector2(0, 1));
|
||||
nudgeSelection(new Vector2(0, 1));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -118,12 +120,29 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnKeyUp(KeyUpEvent e)
|
||||
{
|
||||
base.OnKeyUp(e);
|
||||
|
||||
if (nudgeMovementActive && !e.ControlPressed)
|
||||
{
|
||||
Beatmap.EndChange();
|
||||
nudgeMovementActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints).
|
||||
/// </summary>
|
||||
/// <param name="delta"></param>
|
||||
private void moveSelection(Vector2 delta)
|
||||
private void nudgeSelection(Vector2 delta)
|
||||
{
|
||||
if (!nudgeMovementActive)
|
||||
{
|
||||
nudgeMovementActive = true;
|
||||
Beatmap.BeginChange();
|
||||
}
|
||||
|
||||
var firstBlueprint = SelectionHandler.SelectedBlueprints.FirstOrDefault();
|
||||
|
||||
if (firstBlueprint == null)
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Localisation.HUD;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Screens.Play.HUD
|
||||
@ -21,7 +22,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
private const float bar_height = 10;
|
||||
|
||||
[SettingSource("Show difficulty graph", "Whether a graph displaying difficulty throughout the beatmap should be shown")]
|
||||
[SettingSource(typeof(SongProgressStrings), nameof(SongProgressStrings.ShowGraph), nameof(SongProgressStrings.ShowGraphDescription))]
|
||||
public Bindable<bool> ShowGraph { get; } = new BindableBool(true);
|
||||
|
||||
[Resolved]
|
||||
|
@ -37,11 +37,8 @@ namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
base.Update();
|
||||
|
||||
//We don't want it going to 0 when we pause. so we block the updates
|
||||
if (gameplayClock.IsPaused.Value) return;
|
||||
|
||||
// We want to check Rate every update to cover windup/down
|
||||
Current.Value = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(gameplayClock.CurrentTime).BPM * gameplayClock.Rate;
|
||||
Current.Value = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(gameplayClock.CurrentTime).BPM * gameplayClock.GetTrueGameplayRate();
|
||||
}
|
||||
|
||||
protected override OsuSpriteText CreateSpriteText()
|
||||
|
@ -78,30 +78,39 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
public DefaultHealthDisplay()
|
||||
{
|
||||
Size = new Vector2(1, 5);
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Margin = new MarginPadding { Top = 20 };
|
||||
const float padding = 20;
|
||||
const float bar_height = 5;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
Size = new Vector2(1, bar_height + padding * 2);
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
InternalChild = new Container
|
||||
{
|
||||
new Box
|
||||
Padding = new MarginPadding { Vertical = padding },
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
fill = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0, 1),
|
||||
Masking = true,
|
||||
Children = new[]
|
||||
new Box
|
||||
{
|
||||
new Box
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
fill = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0, 1),
|
||||
Masking = true,
|
||||
Children = new[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Localisation.HUD;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osuTK;
|
||||
|
||||
@ -26,7 +27,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
private readonly DefaultSongProgressGraph graph;
|
||||
private readonly SongProgressInfo info;
|
||||
|
||||
[SettingSource("Show difficulty graph", "Whether a graph displaying difficulty throughout the beatmap should be shown")]
|
||||
[SettingSource(typeof(SongProgressStrings), nameof(SongProgressStrings.ShowGraph), nameof(SongProgressStrings.ShowGraphDescription))]
|
||||
public Bindable<bool> ShowGraph { get; } = new BindableBool(true);
|
||||
|
||||
[Resolved]
|
||||
|
@ -1,18 +1,19 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation.HUD;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
public abstract partial class GameplayAccuracyCounter : PercentageCounter
|
||||
{
|
||||
[SettingSource("Accuracy display mode", "Which accuracy mode should be displayed.")]
|
||||
[SettingSource(typeof(GameplayAccuracyCounterStrings), nameof(GameplayAccuracyCounterStrings.AccuracyDisplay), nameof(GameplayAccuracyCounterStrings.AccuracyDisplayDescription))]
|
||||
public Bindable<AccuracyDisplayMode> AccuracyDisplay { get; } = new Bindable<AccuracyDisplayMode>();
|
||||
|
||||
[Resolved]
|
||||
@ -51,13 +52,13 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
public enum AccuracyDisplayMode
|
||||
{
|
||||
[Description("Standard")]
|
||||
[LocalisableDescription(typeof(GameplayAccuracyCounterStrings), nameof(GameplayAccuracyCounterStrings.AccuracyDisplayModeStandard))]
|
||||
Standard,
|
||||
|
||||
[Description("Maximum achievable")]
|
||||
[LocalisableDescription(typeof(GameplayAccuracyCounterStrings), nameof(GameplayAccuracyCounterStrings.AccuracyDisplayModeMax))]
|
||||
MaximumAchievable,
|
||||
|
||||
[Description("Minimum achievable")]
|
||||
[LocalisableDescription(typeof(GameplayAccuracyCounterStrings), nameof(GameplayAccuracyCounterStrings.AccuracyDisplayModeMin))]
|
||||
MinimumAchievable
|
||||
}
|
||||
}
|
||||
|
@ -12,10 +12,12 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Localisation.HUD;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
@ -25,7 +27,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
[Cached]
|
||||
public partial class BarHitErrorMeter : HitErrorMeter
|
||||
{
|
||||
[SettingSource("Judgement line thickness", "How thick the individual lines should be.")]
|
||||
[SettingSource(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.JudgementLineThickness), nameof(BarHitErrorMeterStrings.JudgementLineThicknessDescription))]
|
||||
public BindableNumber<float> JudgementLineThickness { get; } = new BindableNumber<float>(4)
|
||||
{
|
||||
MinValue = 1,
|
||||
@ -33,16 +35,16 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
Precision = 0.1f,
|
||||
};
|
||||
|
||||
[SettingSource("Show colour bars")]
|
||||
[SettingSource(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.ColourBarVisibility))]
|
||||
public Bindable<bool> ColourBarVisibility { get; } = new Bindable<bool>(true);
|
||||
|
||||
[SettingSource("Show moving average arrow", "Whether an arrow should move beneath the bar showing the average error.")]
|
||||
[SettingSource(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.ShowMovingAverage), nameof(BarHitErrorMeterStrings.ShowMovingAverageDescription))]
|
||||
public Bindable<bool> ShowMovingAverage { get; } = new BindableBool(true);
|
||||
|
||||
[SettingSource("Centre marker style", "How to signify the centre of the display")]
|
||||
[SettingSource(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.CentreMarkerStyle), nameof(BarHitErrorMeterStrings.CentreMarkerStyleDescription))]
|
||||
public Bindable<CentreMarkerStyles> CentreMarkerStyle { get; } = new Bindable<CentreMarkerStyles>(CentreMarkerStyles.Circle);
|
||||
|
||||
[SettingSource("Label style", "How to show early/late extremities")]
|
||||
[SettingSource(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.LabelStyle), nameof(BarHitErrorMeterStrings.LabelStyleDescription))]
|
||||
public Bindable<LabelStyles> LabelStyle { get; } = new Bindable<LabelStyles>(LabelStyles.Icons);
|
||||
|
||||
private const int judgement_line_width = 14;
|
||||
@ -487,15 +489,25 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
|
||||
public enum CentreMarkerStyles
|
||||
{
|
||||
[LocalisableDescription(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.CentreMarkerStylesNone))]
|
||||
None,
|
||||
|
||||
[LocalisableDescription(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.CentreMarkerStylesCircle))]
|
||||
Circle,
|
||||
|
||||
[LocalisableDescription(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.CentreMarkerStylesLine))]
|
||||
Line
|
||||
}
|
||||
|
||||
public enum LabelStyles
|
||||
{
|
||||
[LocalisableDescription(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.LabelStylesNone))]
|
||||
None,
|
||||
|
||||
[LocalisableDescription(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.LabelStylesIcons))]
|
||||
Icons,
|
||||
|
||||
[LocalisableDescription(typeof(BarHitErrorMeterStrings), nameof(BarHitErrorMeterStrings.LabelStylesText))]
|
||||
Text
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Localisation.HUD;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
@ -23,21 +25,21 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
private const int animation_duration = 200;
|
||||
private const int drawable_judgement_size = 8;
|
||||
|
||||
[SettingSource("Judgement count", "The number of displayed judgements")]
|
||||
[SettingSource(typeof(ColourHitErrorMeterStrings), nameof(ColourHitErrorMeterStrings.JudgementCount), nameof(ColourHitErrorMeterStrings.JudgementCountDescription))]
|
||||
public BindableNumber<int> JudgementCount { get; } = new BindableNumber<int>(20)
|
||||
{
|
||||
MinValue = 1,
|
||||
MaxValue = 50,
|
||||
};
|
||||
|
||||
[SettingSource("Judgement spacing", "The space between each displayed judgement")]
|
||||
[SettingSource(typeof(ColourHitErrorMeterStrings), nameof(ColourHitErrorMeterStrings.JudgementSpacing), nameof(ColourHitErrorMeterStrings.JudgementSpacingDescription))]
|
||||
public BindableNumber<float> JudgementSpacing { get; } = new BindableNumber<float>(2)
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
};
|
||||
|
||||
[SettingSource("Judgement shape", "The shape of each displayed judgement")]
|
||||
[SettingSource(typeof(ColourHitErrorMeterStrings), nameof(ColourHitErrorMeterStrings.JudgementShape), nameof(ColourHitErrorMeterStrings.JudgementShapeDescription))]
|
||||
public Bindable<ShapeStyle> JudgementShape { get; } = new Bindable<ShapeStyle>();
|
||||
|
||||
private readonly JudgementFlow judgementsFlow;
|
||||
@ -192,7 +194,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
|
||||
public enum ShapeStyle
|
||||
{
|
||||
[LocalisableDescription(typeof(ColourHitErrorMeterStrings), nameof(ColourHitErrorMeterStrings.ShapeStyleCircle))]
|
||||
Circle,
|
||||
|
||||
[LocalisableDescription(typeof(ColourHitErrorMeterStrings), nameof(ColourHitErrorMeterStrings.ShapeStyleSquare))]
|
||||
Square
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
|
||||
|
||||
public readonly Bindable<bool> ReplayLoaded = new Bindable<bool>();
|
||||
|
||||
private HoldButton button;
|
||||
|
||||
public Action Action { get; set; }
|
||||
@ -60,6 +62,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
HoverGained = () => text.FadeIn(500, Easing.OutQuint),
|
||||
HoverLost = () => text.FadeOut(500, Easing.OutQuint),
|
||||
IsPaused = { BindTarget = IsPaused },
|
||||
ReplayLoaded = { BindTarget = ReplayLoaded },
|
||||
Action = () => Action(),
|
||||
}
|
||||
};
|
||||
@ -110,6 +113,8 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
|
||||
|
||||
public readonly Bindable<bool> ReplayLoaded = new Bindable<bool>();
|
||||
|
||||
protected override bool AllowMultipleFires => true;
|
||||
|
||||
public Action HoverGained;
|
||||
@ -251,7 +256,14 @@ namespace osu.Game.Screens.Play.HUD
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
case GlobalAction.PauseGameplay: // in the future this behaviour will differ for replays etc.
|
||||
if (!pendingAnimation)
|
||||
BeginConfirm();
|
||||
return true;
|
||||
|
||||
case GlobalAction.PauseGameplay:
|
||||
// handled by replay player
|
||||
if (ReplayLoaded.Value) return false;
|
||||
|
||||
if (!pendingAnimation)
|
||||
BeginConfirm();
|
||||
return true;
|
||||
@ -265,7 +277,12 @@ namespace osu.Game.Screens.Play.HUD
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
AbortConfirm();
|
||||
break;
|
||||
|
||||
case GlobalAction.PauseGameplay:
|
||||
if (ReplayLoaded.Value) return;
|
||||
|
||||
AbortConfirm();
|
||||
break;
|
||||
}
|
||||
|
@ -6,7 +6,9 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Localisation.HUD;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
@ -19,16 +21,16 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter
|
||||
|
||||
public bool UsesFixedAnchor { get; set; }
|
||||
|
||||
[SettingSource("Display mode")]
|
||||
[SettingSource(typeof(JudgementCounterDisplayStrings), nameof(JudgementCounterDisplayStrings.JudgementDisplayMode))]
|
||||
public Bindable<DisplayMode> Mode { get; set; } = new Bindable<DisplayMode>();
|
||||
|
||||
[SettingSource("Counter direction")]
|
||||
[SettingSource(typeof(JudgementCounterDisplayStrings), nameof(JudgementCounterDisplayStrings.FlowDirection))]
|
||||
public Bindable<Direction> FlowDirection { get; set; } = new Bindable<Direction>();
|
||||
|
||||
[SettingSource("Show judgement names")]
|
||||
[SettingSource(typeof(JudgementCounterDisplayStrings), nameof(JudgementCounterDisplayStrings.ShowJudgementNames))]
|
||||
public BindableBool ShowJudgementNames { get; set; } = new BindableBool(true);
|
||||
|
||||
[SettingSource("Show max judgement")]
|
||||
[SettingSource(typeof(JudgementCounterDisplayStrings), nameof(JudgementCounterDisplayStrings.ShowMaxJudgement))]
|
||||
public BindableBool ShowMaxJudgement { get; set; } = new BindableBool(true);
|
||||
|
||||
[Resolved]
|
||||
@ -130,8 +132,13 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter
|
||||
|
||||
public enum DisplayMode
|
||||
{
|
||||
[LocalisableDescription(typeof(JudgementCounterDisplayStrings), nameof(JudgementCounterDisplayStrings.JudgementDisplayModeSimple))]
|
||||
Simple,
|
||||
|
||||
[LocalisableDescription(typeof(JudgementCounterDisplayStrings), nameof(JudgementCounterDisplayStrings.JudgementDisplayModeNormal))]
|
||||
Normal,
|
||||
|
||||
[LocalisableDescription(typeof(JudgementCounterDisplayStrings), nameof(JudgementCounterDisplayStrings.JudgementDisplayModeAll))]
|
||||
All
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user