mirror of
https://github.com/osukey/osukey.git
synced 2025-04-29 02:37:25 +09:00
Merge branch 'master' into revert-result-in-playfield
This commit is contained in:
commit
3ee046000e
@ -17,7 +17,7 @@
|
|||||||
<EmbeddedResource Include="Resources\**\*.*" />
|
<EmbeddedResource Include="Resources\**\*.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Code Analysis">
|
<ItemGroup Label="Code Analysis">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" PrivateAssets="All" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Code Analysis">
|
<PropertyGroup Label="Code Analysis">
|
||||||
|
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
|
|
@ -9,9 +9,9 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||||
|
@ -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>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.120.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.131.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -14,7 +13,6 @@ using Android.Content;
|
|||||||
using Android.Content.PM;
|
using Android.Content.PM;
|
||||||
using Android.Graphics;
|
using Android.Graphics;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Provider;
|
|
||||||
using Android.Views;
|
using Android.Views;
|
||||||
using osu.Framework.Android;
|
using osu.Framework.Android;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
@ -131,28 +129,14 @@ namespace osu.Android
|
|||||||
|
|
||||||
await Task.WhenAll(uris.Select(async uri =>
|
await Task.WhenAll(uris.Select(async uri =>
|
||||||
{
|
{
|
||||||
// there are more performant overloads of this method, but this one is the most backwards-compatible
|
var task = await AndroidImportTask.Create(ContentResolver!, uri).ConfigureAwait(false);
|
||||||
// (dates back to API 1).
|
|
||||||
var cursor = ContentResolver?.Query(uri, null, null, null, null);
|
|
||||||
|
|
||||||
if (cursor == null)
|
if (task != 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)
|
|
||||||
{
|
{
|
||||||
tasks.Add(new ImportTask(copy, filename));
|
lock (tasks)
|
||||||
|
{
|
||||||
|
tasks.Add(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})).ConfigureAwait(false);
|
})).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
if (status.Value is UserStatusOnline && activity.Value != null)
|
if (status.Value is UserStatusOnline && activity.Value != null)
|
||||||
{
|
{
|
||||||
presence.State = truncate(activity.Value.Status);
|
presence.State = truncate(activity.Value.GetStatus(privacyMode.Value == DiscordRichPresenceMode.Limited));
|
||||||
presence.Details = truncate(getDetails(activity.Value));
|
presence.Details = truncate(getDetails(activity.Value));
|
||||||
|
|
||||||
if (getBeatmap(activity.Value) is IBeatmapInfo beatmap && beatmap.OnlineID > 0)
|
if (getBeatmap(activity.Value) is IBeatmapInfo beatmap && beatmap.OnlineID > 0)
|
||||||
@ -169,7 +169,7 @@ namespace osu.Desktop
|
|||||||
case UserActivity.InGame game:
|
case UserActivity.InGame game:
|
||||||
return game.BeatmapInfo;
|
return game.BeatmapInfo;
|
||||||
|
|
||||||
case UserActivity.Editing edit:
|
case UserActivity.EditingBeatmap edit:
|
||||||
return edit.BeatmapInfo;
|
return edit.BeatmapInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,9 +183,12 @@ namespace osu.Desktop
|
|||||||
case UserActivity.InGame game:
|
case UserActivity.InGame game:
|
||||||
return game.BeatmapInfo.ToString() ?? string.Empty;
|
return game.BeatmapInfo.ToString() ?? string.Empty;
|
||||||
|
|
||||||
case UserActivity.Editing edit:
|
case UserActivity.EditingBeatmap edit:
|
||||||
return edit.BeatmapInfo.ToString() ?? string.Empty;
|
return edit.BeatmapInfo.ToString() ?? string.Empty;
|
||||||
|
|
||||||
|
case UserActivity.WatchingReplay watching:
|
||||||
|
return watching.BeatmapInfo.ToString();
|
||||||
|
|
||||||
case UserActivity.InLobby lobby:
|
case UserActivity.InLobby lobby:
|
||||||
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
|
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ namespace osu.Desktop
|
|||||||
internal partial class OsuGameDesktop : OsuGame
|
internal partial class OsuGameDesktop : OsuGame
|
||||||
{
|
{
|
||||||
private OsuSchemeLinkIPCChannel? osuSchemeLinkIPCChannel;
|
private OsuSchemeLinkIPCChannel? osuSchemeLinkIPCChannel;
|
||||||
|
private ArchiveImportIPCChannel? archiveImportIPCChannel;
|
||||||
|
|
||||||
public OsuGameDesktop(string[]? args = null)
|
public OsuGameDesktop(string[]? args = null)
|
||||||
: base(args)
|
: base(args)
|
||||||
@ -123,6 +124,7 @@ namespace osu.Desktop
|
|||||||
LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
|
LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
|
||||||
|
|
||||||
osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this);
|
osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this);
|
||||||
|
archiveImportIPCChannel = new ArchiveImportIPCChannel(Host, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetHost(GameHost host)
|
public override void SetHost(GameHost host)
|
||||||
@ -181,6 +183,7 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
osuSchemeLinkIPCChannel?.Dispose();
|
osuSchemeLinkIPCChannel?.Dispose();
|
||||||
|
archiveImportIPCChannel?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SDL2BatteryInfo : BatteryInfo
|
private class SDL2BatteryInfo : BatteryInfo
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
||||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||||
<PackageReference Include="System.IO.Packaging" Version="6.0.0" />
|
<PackageReference Include="System.IO.Packaging" Version="7.0.0" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.1.1.14" />
|
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.4" />
|
||||||
<PackageReference Include="nunit" Version="3.13.3" />
|
<PackageReference Include="nunit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
switch (targetComponent.Lookup)
|
switch (targetComponent.Lookup)
|
||||||
{
|
{
|
||||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
||||||
var components = base.GetDrawableComponent(lookup) as SkinnableTargetComponentsContainer;
|
var components = base.GetDrawableComponent(lookup) as Container;
|
||||||
|
|
||||||
if (providesComboCounter && components != null)
|
if (providesComboCounter && components != null)
|
||||||
{
|
{
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
@ -14,4 +14,6 @@ Hit200: mania/hit200@2x
|
|||||||
Hit300: mania/hit300@2x
|
Hit300: mania/hit300@2x
|
||||||
Hit300g: mania/hit300g@2x
|
Hit300g: mania/hit300g@2x
|
||||||
StageLeft: mania/stage-left
|
StageLeft: mania/stage-left
|
||||||
StageRight: mania/stage-right
|
StageRight: mania/stage-right
|
||||||
|
NoteImage0L: LongNoteTailWang
|
||||||
|
NoteImage1L: LongNoteTailWang
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class ManiaScrollSlider : OsuSliderBar<double>
|
private partial class ManiaScrollSlider : RoundedSliderBar<double>
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(Current.Value, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / Current.Value));
|
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(Current.Value, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / Current.Value));
|
||||||
}
|
}
|
||||||
|
@ -374,7 +374,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
|
|
||||||
protected override void OnFree()
|
protected override void OnFree()
|
||||||
{
|
{
|
||||||
slidingSample.Samples = null;
|
slidingSample.ClearSamples();
|
||||||
base.OnFree();
|
base.OnFree();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,11 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
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<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
|
|
||||||
private readonly Box colouredBox;
|
private readonly Box shadeBackground;
|
||||||
private readonly Box shadow;
|
private readonly Box shadeForeground;
|
||||||
|
|
||||||
public ArgonHoldNoteTailPiece()
|
public ArgonHoldNoteTailPiece()
|
||||||
{
|
{
|
||||||
@ -32,32 +32,25 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
shadow = new Box
|
shadeBackground = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Height = 0.82f,
|
Height = ArgonNotePiece.NOTE_ACCENT_RATIO,
|
||||||
Masking = true,
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
||||||
|
Masking = true,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
colouredBox = new Box
|
shadeForeground = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
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)
|
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
{
|
{
|
||||||
colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up
|
Scale = new Vector2(1, direction.NewValue == ScrollingDirection.Up ? -1 : 1);
|
||||||
? Anchor.TopCentre
|
|
||||||
: Anchor.BottomCentre;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
||||||
{
|
{
|
||||||
colouredBox.Colour = ColourInfo.GradientVertical(
|
shadeBackground.Colour = accent.NewValue.Darken(1.7f);
|
||||||
accent.NewValue,
|
shadeForeground.Colour = accent.NewValue.Darken(1.1f);
|
||||||
accent.NewValue.Darken(0.1f)
|
|
||||||
);
|
|
||||||
|
|
||||||
shadow.Colour = accent.NewValue.Darken(0.5f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
internal partial class ArgonNotePiece : CompositeDrawable
|
internal partial class ArgonNotePiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
public const float NOTE_HEIGHT = 42;
|
public const float NOTE_HEIGHT = 42;
|
||||||
|
public const float NOTE_ACCENT_RATIO = 0.82f;
|
||||||
public const float CORNER_RADIUS = 3.4f;
|
public const float CORNER_RADIUS = 3.4f;
|
||||||
|
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Height = 0.82f,
|
Height = NOTE_ACCENT_RATIO,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
CornerRadius = CORNER_RADIUS,
|
CornerRadius = CORNER_RADIUS,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -95,6 +95,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up
|
colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up
|
||||||
? Anchor.TopCentre
|
? Anchor.TopCentre
|
||||||
: Anchor.BottomCentre;
|
: Anchor.BottomCentre;
|
||||||
|
|
||||||
|
Scale = new Vector2(1, direction.NewValue == ScrollingDirection.Up ? -1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
||||||
|
@ -2,12 +2,15 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Animations;
|
using osu.Framework.Graphics.Animations;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
@ -34,6 +37,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
private Drawable? lightContainer;
|
private Drawable? lightContainer;
|
||||||
|
|
||||||
private Drawable? light;
|
private Drawable? light;
|
||||||
|
private LegacyNoteBodyStyle? bodyStyle;
|
||||||
|
|
||||||
public LegacyBodyPiece()
|
public LegacyBodyPiece()
|
||||||
{
|
{
|
||||||
@ -80,7 +84,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bodySprite = skin.GetAnimation(imageName, WrapMode.ClampToEdge, WrapMode.ClampToEdge, true, true).With(d =>
|
bodyStyle = skin.GetConfig<ManiaSkinConfigurationLookup, LegacyNoteBodyStyle>(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.NoteBodyStyle))?.Value;
|
||||||
|
|
||||||
|
var wrapMode = bodyStyle == LegacyNoteBodyStyle.Stretch ? WrapMode.ClampToEdge : WrapMode.Repeat;
|
||||||
|
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
isHitting.BindTo(holdNote.IsHitting);
|
||||||
|
|
||||||
|
bodySprite = skin.GetAnimation(imageName, wrapMode, wrapMode, true, true).With(d =>
|
||||||
{
|
{
|
||||||
if (d == null)
|
if (d == null)
|
||||||
return;
|
return;
|
||||||
@ -91,15 +102,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
d.Anchor = Anchor.TopCentre;
|
d.Anchor = Anchor.TopCentre;
|
||||||
d.RelativeSizeAxes = Axes.Both;
|
d.RelativeSizeAxes = Axes.Both;
|
||||||
d.Size = Vector2.One;
|
d.Size = Vector2.One;
|
||||||
d.FillMode = FillMode.Stretch;
|
// Todo: Wrap?
|
||||||
// Todo: Wrap
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bodySprite != null)
|
if (bodySprite != null)
|
||||||
InternalChild = bodySprite;
|
InternalChild = bodySprite;
|
||||||
|
|
||||||
direction.BindTo(scrollingInfo.Direction);
|
|
||||||
isHitting.BindTo(holdNote.IsHitting);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -161,7 +168,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
if (bodySprite != null)
|
if (bodySprite != null)
|
||||||
{
|
{
|
||||||
bodySprite.Origin = Anchor.BottomCentre;
|
bodySprite.Origin = Anchor.BottomCentre;
|
||||||
bodySprite.Scale = new Vector2(1, -1);
|
bodySprite.Scale = new Vector2(bodySprite.Scale.X, Math.Abs(bodySprite.Scale.Y) * -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (light != null)
|
if (light != null)
|
||||||
@ -172,7 +179,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
if (bodySprite != null)
|
if (bodySprite != null)
|
||||||
{
|
{
|
||||||
bodySprite.Origin = Anchor.TopCentre;
|
bodySprite.Origin = Anchor.TopCentre;
|
||||||
bodySprite.Scale = Vector2.One;
|
bodySprite.Scale = new Vector2(bodySprite.Scale.X, Math.Abs(bodySprite.Scale.Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (light != null)
|
if (light != null)
|
||||||
@ -203,6 +210,29 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
missFadeTime.Value ??= holdNote.HoldBrokenTime;
|
missFadeTime.Value ??= holdNote.HoldBrokenTime;
|
||||||
|
|
||||||
|
// here we go...
|
||||||
|
switch (bodyStyle)
|
||||||
|
{
|
||||||
|
case LegacyNoteBodyStyle.Stretch:
|
||||||
|
// this is how lazer works by default. nothing required.
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// this is where things get fucked up.
|
||||||
|
// honestly there's three modes to handle here but they seem really pointless?
|
||||||
|
// let's wait to see if anyone actually uses them in skins.
|
||||||
|
if (bodySprite != null)
|
||||||
|
{
|
||||||
|
var sprite = bodySprite as Sprite ?? bodySprite.ChildrenOfType<Sprite>().Single();
|
||||||
|
|
||||||
|
bodySprite.FillMode = FillMode.Stretch;
|
||||||
|
// i dunno this looks about right??
|
||||||
|
bodySprite.Scale = new Vector2(1, 32800 / sprite.DrawHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
public partial class TestScenePathControlPointVisualiser : OsuManualInputManagerTestScene
|
public partial class TestScenePathControlPointVisualiser : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private Slider slider;
|
private Slider slider;
|
||||||
private PathControlPointVisualiser visualiser;
|
private PathControlPointVisualiser<Slider> visualiser;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup() => Schedule(() =>
|
public void Setup() => Schedule(() =>
|
||||||
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertControlPointPathType(3, null);
|
assertControlPointPathType(3, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser(slider, allowSelection)
|
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser<Slider>(slider, allowSelection)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre
|
Origin = Anchor.Centre
|
||||||
|
@ -159,11 +159,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assertSelectionCount(int count) =>
|
private void assertSelectionCount(int count) =>
|
||||||
AddAssert($"{count} control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == count);
|
AddAssert($"{count} control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == count);
|
||||||
|
|
||||||
private void assertSelected(int index) =>
|
private void assertSelected(int index) =>
|
||||||
AddAssert($"{(index + 1).ToOrdinalWords()} control point piece selected",
|
AddAssert($"{(index + 1).ToOrdinalWords()} control point piece selected",
|
||||||
() => this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[index]).IsSelected.Value);
|
() => this.ChildrenOfType<PathControlPointPiece<Slider>>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[index]).IsSelected.Value);
|
||||||
|
|
||||||
private void moveMouseToRelativePosition(Vector2 relativePosition) =>
|
private void moveMouseToRelativePosition(Vector2 relativePosition) =>
|
||||||
AddStep($"move mouse to {relativePosition}", () =>
|
AddStep($"move mouse to {relativePosition}", () =>
|
||||||
@ -202,12 +202,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
moveMouseToControlPoint(2);
|
moveMouseToControlPoint(2);
|
||||||
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
|
||||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
|
||||||
|
|
||||||
addMovementStep(new Vector2(450, 50));
|
addMovementStep(new Vector2(450, 50));
|
||||||
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
|
||||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
|
||||||
|
|
||||||
assertControlPointPosition(2, new Vector2(450, 50));
|
assertControlPointPosition(2, new Vector2(450, 50));
|
||||||
assertControlPointType(2, PathType.PerfectCurve);
|
assertControlPointType(2, PathType.PerfectCurve);
|
||||||
@ -236,12 +236,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
moveMouseToControlPoint(3);
|
moveMouseToControlPoint(3);
|
||||||
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
|
||||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
|
||||||
|
|
||||||
addMovementStep(new Vector2(550, 50));
|
addMovementStep(new Vector2(550, 50));
|
||||||
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
|
||||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
|
||||||
|
|
||||||
// note: if the head is part of the selection being moved, the entire slider is moved.
|
// note: if the head is part of the selection being moved, the entire slider is moved.
|
||||||
// the unselected nodes will therefore change position relative to the slider head.
|
// the unselected nodes will therefore change position relative to the slider head.
|
||||||
@ -354,7 +354,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
public new SliderBodyPiece BodyPiece => base.BodyPiece;
|
public new SliderBodyPiece BodyPiece => base.BodyPiece;
|
||||||
public new TestSliderCircleOverlay HeadOverlay => (TestSliderCircleOverlay)base.HeadOverlay;
|
public new TestSliderCircleOverlay HeadOverlay => (TestSliderCircleOverlay)base.HeadOverlay;
|
||||||
public new TestSliderCircleOverlay TailOverlay => (TestSliderCircleOverlay)base.TailOverlay;
|
public new TestSliderCircleOverlay TailOverlay => (TestSliderCircleOverlay)base.TailOverlay;
|
||||||
public new PathControlPointVisualiser ControlPointVisualiser => base.ControlPointVisualiser;
|
public new PathControlPointVisualiser<Slider> ControlPointVisualiser => base.ControlPointVisualiser;
|
||||||
|
|
||||||
public TestSliderBlueprint(Slider slider)
|
public TestSliderBlueprint(Slider slider)
|
||||||
: base(slider)
|
: base(slider)
|
||||||
|
@ -199,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
public new SliderBodyPiece BodyPiece => base.BodyPiece;
|
public new SliderBodyPiece BodyPiece => base.BodyPiece;
|
||||||
public new TestSliderCircleOverlay HeadOverlay => (TestSliderCircleOverlay)base.HeadOverlay;
|
public new TestSliderCircleOverlay HeadOverlay => (TestSliderCircleOverlay)base.HeadOverlay;
|
||||||
public new TestSliderCircleOverlay TailOverlay => (TestSliderCircleOverlay)base.TailOverlay;
|
public new TestSliderCircleOverlay TailOverlay => (TestSliderCircleOverlay)base.TailOverlay;
|
||||||
public new PathControlPointVisualiser ControlPointVisualiser => base.ControlPointVisualiser;
|
public new PathControlPointVisualiser<Slider> ControlPointVisualiser => base.ControlPointVisualiser;
|
||||||
|
|
||||||
public TestSliderBlueprint(Slider slider)
|
public TestSliderBlueprint(Slider slider)
|
||||||
: base(slider)
|
: base(slider)
|
||||||
|
@ -72,14 +72,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestMovingUnsnappedSliderNodesSnaps()
|
public void TestMovingUnsnappedSliderNodesSnaps()
|
||||||
{
|
{
|
||||||
PathControlPointPiece sliderEnd = null;
|
PathControlPointPiece<Slider> sliderEnd = null;
|
||||||
|
|
||||||
assertSliderSnapped(false);
|
assertSliderSnapped(false);
|
||||||
|
|
||||||
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
AddStep("select slider end", () =>
|
AddStep("select slider end", () =>
|
||||||
{
|
{
|
||||||
sliderEnd = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints.Last());
|
sliderEnd = this.ChildrenOfType<PathControlPointPiece<Slider>>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints.Last());
|
||||||
InputManager.MoveMouseTo(sliderEnd.ScreenSpaceDrawQuad.Centre);
|
InputManager.MoveMouseTo(sliderEnd.ScreenSpaceDrawQuad.Centre);
|
||||||
});
|
});
|
||||||
AddStep("move slider end", () =>
|
AddStep("move slider end", () =>
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
AddStep("move mouse to new point location", () =>
|
AddStep("move mouse to new point location", () =>
|
||||||
{
|
{
|
||||||
var firstPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[0]);
|
var firstPiece = this.ChildrenOfType<PathControlPointPiece<Slider>>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[0]);
|
||||||
var pos = slider.Path.PositionAt(0.25d) + slider.Position;
|
var pos = slider.Path.PositionAt(0.25d) + slider.Position;
|
||||||
InputManager.MoveMouseTo(firstPiece.Parent.ToScreenSpace(pos));
|
InputManager.MoveMouseTo(firstPiece.Parent.ToScreenSpace(pos));
|
||||||
});
|
});
|
||||||
@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
AddStep("move mouse to second control point", () =>
|
AddStep("move mouse to second control point", () =>
|
||||||
{
|
{
|
||||||
var secondPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[1]);
|
var secondPiece = this.ChildrenOfType<PathControlPointPiece<Slider>>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[1]);
|
||||||
InputManager.MoveMouseTo(secondPiece);
|
InputManager.MoveMouseTo(secondPiece);
|
||||||
});
|
});
|
||||||
AddStep("quick delete", () =>
|
AddStep("quick delete", () =>
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
|
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
|
||||||
|
|
||||||
private Slider? slider;
|
private Slider? slider;
|
||||||
private PathControlPointVisualiser? visualiser;
|
private PathControlPointVisualiser<Slider>? visualiser;
|
||||||
|
|
||||||
private const double split_gap = 100;
|
private const double split_gap = 100;
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddStep("select added slider", () =>
|
AddStep("select added slider", () =>
|
||||||
{
|
{
|
||||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||||
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser>().First();
|
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser<Slider>>().First();
|
||||||
});
|
});
|
||||||
|
|
||||||
moveMouseToControlPoint(2);
|
moveMouseToControlPoint(2);
|
||||||
@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddStep("select added slider", () =>
|
AddStep("select added slider", () =>
|
||||||
{
|
{
|
||||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||||
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser>().First();
|
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser<Slider>>().First();
|
||||||
});
|
});
|
||||||
|
|
||||||
moveMouseToControlPoint(2);
|
moveMouseToControlPoint(2);
|
||||||
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddStep("select added slider", () =>
|
AddStep("select added slider", () =>
|
||||||
{
|
{
|
||||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||||
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser>().First();
|
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser<Slider>>().First();
|
||||||
});
|
});
|
||||||
|
|
||||||
moveMouseToControlPoint(2);
|
moveMouseToControlPoint(2);
|
||||||
|
@ -5,9 +5,11 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
@ -22,6 +24,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
private int depthIndex;
|
private int depthIndex;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestHits()
|
public void TestHits()
|
||||||
{
|
{
|
||||||
@ -56,6 +61,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddStep("Hit stream late", () => SetContents(_ => testStream(5, true, 150)));
|
AddStep("Hit stream late", () => SetContents(_ => testStream(5, true, 150)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHitLighting()
|
||||||
|
{
|
||||||
|
AddToggleStep("toggle hit lighting", v => config.SetValue(OsuSetting.HitLighting, v));
|
||||||
|
AddStep("Hit Big Single", () => SetContents(_ => testSingle(2, true)));
|
||||||
|
}
|
||||||
|
|
||||||
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
||||||
{
|
{
|
||||||
var playfield = new TestOsuPlayfield();
|
var playfield = new TestOsuPlayfield();
|
||||||
|
@ -4,29 +4,38 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public partial class TestSceneHitCircleKiai : TestSceneHitCircle
|
public partial class TestSceneHitCircleKiai : TestSceneHitCircle, IBeatSyncProvider
|
||||||
{
|
{
|
||||||
|
private ControlPointInfo controlPoints { get; set; }
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
var controlPointInfo = new ControlPointInfo();
|
controlPoints = new ControlPointInfo();
|
||||||
|
|
||||||
controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
|
controlPoints.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||||
|
|
||||||
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
|
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPointInfo
|
ControlPointInfo = controlPoints
|
||||||
});
|
});
|
||||||
|
|
||||||
// track needs to be playing for BeatSyncedContainer to work.
|
// track needs to be playing for BeatSyncedContainer to work.
|
||||||
Beatmap.Value.Track.Start();
|
Beatmap.Value.Track.Start();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ChannelAmplitudes IHasAmplitudes.CurrentAmplitudes => new ChannelAmplitudes();
|
||||||
|
ControlPointInfo IBeatSyncProvider.ControlPoints => controlPoints;
|
||||||
|
IClock IBeatSyncProvider.Clock => Clock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,14 @@ using osu.Framework.Input.Bindings;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Input.States;
|
using osu.Framework.Input.States;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -22,7 +29,7 @@ using osuTK.Graphics;
|
|||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public partial class TestSceneTouchInput : OsuManualInputManagerTestScene
|
public partial class TestSceneOsuTouchInput : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager config { get; set; } = null!;
|
private OsuConfigManager config { get; set; } = null!;
|
||||||
@ -33,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private OsuInputManager osuInputManager = null!;
|
private OsuInputManager osuInputManager = null!;
|
||||||
|
|
||||||
|
private Container mainContent = null!;
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public void SetUpSteps()
|
public void SetUpSteps()
|
||||||
{
|
{
|
||||||
@ -44,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
osuInputManager = new OsuInputManager(new OsuRuleset().RulesetInfo)
|
osuInputManager = new OsuInputManager(new OsuRuleset().RulesetInfo)
|
||||||
{
|
{
|
||||||
Child = new Container
|
Child = mainContent = new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -54,13 +63,19 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
|
Depth = float.MinValue,
|
||||||
X = -100,
|
X = -100,
|
||||||
},
|
},
|
||||||
rightKeyCounter = new TestActionKeyCounter(OsuAction.RightButton)
|
rightKeyCounter = new TestActionKeyCounter(OsuAction.RightButton)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
|
Depth = float.MinValue,
|
||||||
X = 100,
|
X = 100,
|
||||||
|
},
|
||||||
|
new OsuCursorContainer
|
||||||
|
{
|
||||||
|
Depth = float.MinValue,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -70,6 +85,40 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStreamInputVisual()
|
||||||
|
{
|
||||||
|
addHitCircleAt(TouchSource.Touch1);
|
||||||
|
addHitCircleAt(TouchSource.Touch2);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
|
||||||
|
endTouch(TouchSource.Touch1);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
AddRepeatStep("Alternate", () =>
|
||||||
|
{
|
||||||
|
TouchSource down = i % 2 == 0 ? TouchSource.Touch3 : TouchSource.Touch4;
|
||||||
|
TouchSource up = i % 2 == 0 ? TouchSource.Touch4 : TouchSource.Touch3;
|
||||||
|
|
||||||
|
// sometimes the user will end the previous touch before touching again, sometimes not.
|
||||||
|
if (RNG.NextBool())
|
||||||
|
{
|
||||||
|
InputManager.BeginTouch(new Touch(down, getSanePositionForSource(down)));
|
||||||
|
InputManager.EndTouch(new Touch(up, getSanePositionForSource(up)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InputManager.EndTouch(new Touch(up, getSanePositionForSource(up)));
|
||||||
|
InputManager.BeginTouch(new Touch(down, getSanePositionForSource(down)));
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSimpleInput()
|
public void TestSimpleInput()
|
||||||
{
|
{
|
||||||
@ -116,9 +165,224 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
endTouch(TouchSource.Touch2);
|
endTouch(TouchSource.Touch2);
|
||||||
checkPosition(TouchSource.Touch2);
|
checkPosition(TouchSource.Touch2);
|
||||||
|
|
||||||
// note that touch1 was never ended, but becomes active for tracking again.
|
// note that touch1 was never ended, but is no longer valid for touch input due to touch 2 occurring.
|
||||||
beginTouch(TouchSource.Touch1);
|
beginTouch(TouchSource.Touch1);
|
||||||
|
checkPosition(TouchSource.Touch2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStreamInput()
|
||||||
|
{
|
||||||
|
// In this scenario, the user is tapping on the first object in a stream,
|
||||||
|
// then using one or two fingers in empty space to continue the stream.
|
||||||
|
|
||||||
|
addHitCircleAt(TouchSource.Touch1);
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
|
||||||
|
// The first touch is handled as normal.
|
||||||
|
assertKeyCounter(1, 0);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
checkPosition(TouchSource.Touch1);
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
// The second touch should release the first, and also act as a right button.
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
|
||||||
|
assertKeyCounter(1, 1);
|
||||||
|
// Importantly, this is different from the simple case because an object was interacted with in the first touch, but not the second touch.
|
||||||
|
// left button is automatically released.
|
||||||
|
checkNotPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
// Also importantly, the positional part of the second touch is ignored.
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
// In this scenario, a third touch should be allowed, and handled similarly to the second.
|
||||||
|
beginTouch(TouchSource.Touch3);
|
||||||
|
|
||||||
|
assertKeyCounter(2, 1);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
// Position is still ignored.
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
endTouch(TouchSource.Touch2);
|
||||||
|
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkNotPressed(OsuAction.RightButton);
|
||||||
|
// Position is still ignored.
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
// User continues streaming
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
|
||||||
|
assertKeyCounter(2, 2);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
// Position is still ignored.
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
// In this mode a maximum of three touches should be supported.
|
||||||
|
// A fourth touch should result in no changes anywhere.
|
||||||
|
beginTouch(TouchSource.Touch4);
|
||||||
|
assertKeyCounter(2, 2);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
endTouch(TouchSource.Touch4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStreamInputWithInitialTouchDownLeft()
|
||||||
|
{
|
||||||
|
// In this scenario, the user is wanting to use stream input but we start with one finger still on the screen.
|
||||||
|
// That finger is mapped to a left action.
|
||||||
|
|
||||||
|
addHitCircleAt(TouchSource.Touch2);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
assertKeyCounter(1, 0);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
// hits circle as right action
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
assertKeyCounter(1, 1);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch2);
|
||||||
|
|
||||||
|
endTouch(TouchSource.Touch1);
|
||||||
|
checkNotPressed(OsuAction.LeftButton);
|
||||||
|
|
||||||
|
// stream using other two fingers while touch2 tracks
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
assertKeyCounter(2, 1);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
// right button is automatically released
|
||||||
|
checkNotPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch2);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch3);
|
||||||
|
assertKeyCounter(2, 2);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch2);
|
||||||
|
|
||||||
|
endTouch(TouchSource.Touch1);
|
||||||
|
checkNotPressed(OsuAction.LeftButton);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
assertKeyCounter(3, 2);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStreamInputWithInitialTouchDownRight()
|
||||||
|
{
|
||||||
|
// In this scenario, the user is wanting to use stream input but we start with one finger still on the screen.
|
||||||
|
// That finger is mapped to a right action.
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
|
||||||
|
assertKeyCounter(1, 1);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
|
||||||
|
endTouch(TouchSource.Touch1);
|
||||||
|
|
||||||
|
addHitCircleAt(TouchSource.Touch1);
|
||||||
|
|
||||||
|
// hits circle as left action
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
assertKeyCounter(2, 1);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
endTouch(TouchSource.Touch2);
|
||||||
|
|
||||||
|
// stream using other two fingers while touch1 tracks
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
assertKeyCounter(2, 2);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
// left button is automatically released
|
||||||
|
checkNotPressed(OsuAction.LeftButton);
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch3);
|
||||||
|
assertKeyCounter(3, 2);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
endTouch(TouchSource.Touch2);
|
||||||
|
checkNotPressed(OsuAction.RightButton);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
assertKeyCounter(3, 3);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNonStreamOverlappingDirectTouchesWithRelease()
|
||||||
|
{
|
||||||
|
// In this scenario, the user is tapping on three circles directly while correctly releasing the first touch.
|
||||||
|
// All three should be recognised.
|
||||||
|
|
||||||
|
addHitCircleAt(TouchSource.Touch1);
|
||||||
|
addHitCircleAt(TouchSource.Touch2);
|
||||||
|
addHitCircleAt(TouchSource.Touch3);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
assertKeyCounter(1, 0);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
assertKeyCounter(1, 1);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch2);
|
||||||
|
|
||||||
|
endTouch(TouchSource.Touch1);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch3);
|
||||||
|
assertKeyCounter(2, 1);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNonStreamOverlappingDirectTouchesWithoutRelease()
|
||||||
|
{
|
||||||
|
// In this scenario, the user is tapping on three circles directly without releasing any touches.
|
||||||
|
// The first two should be recognised, but a third should not (as the user already has two fingers down).
|
||||||
|
|
||||||
|
addHitCircleAt(TouchSource.Touch1);
|
||||||
|
addHitCircleAt(TouchSource.Touch2);
|
||||||
|
addHitCircleAt(TouchSource.Touch3);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
assertKeyCounter(1, 0);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
assertKeyCounter(1, 1);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch2);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch3);
|
||||||
|
assertKeyCounter(1, 1);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
checkPosition(TouchSource.Touch3);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -263,6 +527,22 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
assertKeyCounter(1, 1);
|
assertKeyCounter(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addHitCircleAt(TouchSource source)
|
||||||
|
{
|
||||||
|
AddStep($"Add circle at {source}", () =>
|
||||||
|
{
|
||||||
|
var hitCircle = new HitCircle();
|
||||||
|
|
||||||
|
hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
mainContent.Add(new DrawableHitCircle(hitCircle)
|
||||||
|
{
|
||||||
|
Clock = new FramedClock(new ManualClock()),
|
||||||
|
Position = mainContent.ToLocalSpace(getSanePositionForSource(source)),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void beginTouch(TouchSource source, Vector2? screenSpacePosition = null) =>
|
private void beginTouch(TouchSource source, Vector2? screenSpacePosition = null) =>
|
||||||
AddStep($"Begin touch for {source}", () => InputManager.BeginTouch(new Touch(source, screenSpacePosition ??= getSanePositionForSource(source))));
|
AddStep($"Begin touch for {source}", () => InputManager.BeginTouch(new Touch(source, screenSpacePosition ??= getSanePositionForSource(source))));
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="Moq" Version="4.18.2" />
|
<PackageReference Include="Moq" Version="4.18.4" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -8,34 +8,36 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Lines;
|
using osu.Framework.Graphics.Lines;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A visualisation of the line between two <see cref="PathControlPointPiece"/>s.
|
/// A visualisation of the line between two <see cref="PathControlPointPiece{T}"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PathControlPointConnectionPiece : CompositeDrawable
|
/// <typeparam name="T">The type of <see cref="OsuHitObject"/> which this <see cref="PathControlPointConnectionPiece{T}"/> visualises.</typeparam>
|
||||||
|
public partial class PathControlPointConnectionPiece<T> : CompositeDrawable where T : OsuHitObject, IHasPath
|
||||||
{
|
{
|
||||||
public readonly PathControlPoint ControlPoint;
|
public readonly PathControlPoint ControlPoint;
|
||||||
|
|
||||||
private readonly Path path;
|
private readonly Path path;
|
||||||
private readonly Slider slider;
|
private readonly T hitObject;
|
||||||
public int ControlPointIndex { get; set; }
|
public int ControlPointIndex { get; set; }
|
||||||
|
|
||||||
private IBindable<Vector2> sliderPosition;
|
private IBindable<Vector2> hitObjectPosition;
|
||||||
private IBindable<int> pathVersion;
|
private IBindable<int> pathVersion;
|
||||||
|
|
||||||
public PathControlPointConnectionPiece(Slider slider, int controlPointIndex)
|
public PathControlPointConnectionPiece(T hitObject, int controlPointIndex)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
this.hitObject = hitObject;
|
||||||
ControlPointIndex = controlPointIndex;
|
ControlPointIndex = controlPointIndex;
|
||||||
|
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
ControlPoint = slider.Path.ControlPoints[controlPointIndex];
|
ControlPoint = hitObject.Path.ControlPoints[controlPointIndex];
|
||||||
|
|
||||||
InternalChild = path = new SmoothPath
|
InternalChild = path = new SmoothPath
|
||||||
{
|
{
|
||||||
@ -48,10 +50,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
sliderPosition = slider.PositionBindable.GetBoundCopy();
|
hitObjectPosition = hitObject.PositionBindable.GetBoundCopy();
|
||||||
sliderPosition.BindValueChanged(_ => updateConnectingPath());
|
hitObjectPosition.BindValueChanged(_ => updateConnectingPath());
|
||||||
|
|
||||||
pathVersion = slider.Path.Version.GetBoundCopy();
|
pathVersion = hitObject.Path.Version.GetBoundCopy();
|
||||||
pathVersion.BindValueChanged(_ => updateConnectingPath());
|
pathVersion.BindValueChanged(_ => updateConnectingPath());
|
||||||
|
|
||||||
updateConnectingPath();
|
updateConnectingPath();
|
||||||
@ -62,16 +64,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void updateConnectingPath()
|
private void updateConnectingPath()
|
||||||
{
|
{
|
||||||
Position = slider.StackedPosition + ControlPoint.Position;
|
Position = hitObject.StackedPosition + ControlPoint.Position;
|
||||||
|
|
||||||
path.ClearVertices();
|
path.ClearVertices();
|
||||||
|
|
||||||
int nextIndex = ControlPointIndex + 1;
|
int nextIndex = ControlPointIndex + 1;
|
||||||
if (nextIndex == 0 || nextIndex >= slider.Path.ControlPoints.Count)
|
if (nextIndex == 0 || nextIndex >= hitObject.Path.ControlPoints.Count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
path.AddVertex(Vector2.Zero);
|
path.AddVertex(Vector2.Zero);
|
||||||
path.AddVertex(slider.Path.ControlPoints[nextIndex].Position - ControlPoint.Position);
|
path.AddVertex(hitObject.Path.ControlPoints[nextIndex].Position - ControlPoint.Position);
|
||||||
|
|
||||||
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
||||||
}
|
}
|
||||||
|
@ -29,11 +29,13 @@ using osuTK.Input;
|
|||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A visualisation of a single <see cref="PathControlPoint"/> in a <see cref="Slider"/>.
|
/// A visualisation of a single <see cref="PathControlPoint"/> in an osu hit object with a path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PathControlPointPiece : BlueprintPiece<Slider>, IHasTooltip
|
/// <typeparam name="T">The type of <see cref="OsuHitObject"/> which this <see cref="PathControlPointPiece{T}"/> visualises.</typeparam>
|
||||||
|
public partial class PathControlPointPiece<T> : BlueprintPiece<T>, IHasTooltip
|
||||||
|
where T : OsuHitObject, IHasPath
|
||||||
{
|
{
|
||||||
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
public Action<PathControlPointPiece<T>, MouseButtonEvent> RequestSelection;
|
||||||
|
|
||||||
public Action<PathControlPoint> DragStarted;
|
public Action<PathControlPoint> DragStarted;
|
||||||
public Action<DragEvent> DragInProgress;
|
public Action<DragEvent> DragInProgress;
|
||||||
@ -44,34 +46,34 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
public readonly BindableBool IsSelected = new BindableBool();
|
public readonly BindableBool IsSelected = new BindableBool();
|
||||||
public readonly PathControlPoint ControlPoint;
|
public readonly PathControlPoint ControlPoint;
|
||||||
|
|
||||||
private readonly Slider slider;
|
private readonly T hitObject;
|
||||||
private readonly Container marker;
|
private readonly Container marker;
|
||||||
private readonly Drawable markerRing;
|
private readonly Drawable markerRing;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuColour colours { get; set; }
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
private IBindable<Vector2> sliderPosition;
|
private IBindable<Vector2> hitObjectPosition;
|
||||||
private IBindable<float> sliderScale;
|
private IBindable<float> hitObjectScale;
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
private readonly IBindable<int> sliderVersion;
|
private readonly IBindable<int> hitObjectVersion;
|
||||||
|
|
||||||
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
|
public PathControlPointPiece(T hitObject, PathControlPoint controlPoint)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
this.hitObject = hitObject;
|
||||||
ControlPoint = controlPoint;
|
ControlPoint = controlPoint;
|
||||||
|
|
||||||
// we don't want to run the path type update on construction as it may inadvertently change the slider.
|
// we don't want to run the path type update on construction as it may inadvertently change the hit object.
|
||||||
cachePoints(slider);
|
cachePoints(hitObject);
|
||||||
|
|
||||||
sliderVersion = slider.Path.Version.GetBoundCopy();
|
hitObjectVersion = hitObject.Path.Version.GetBoundCopy();
|
||||||
|
|
||||||
// schedule ensure that updates are only applied after all operations from a single frame are applied.
|
// schedule ensure that updates are only applied after all operations from a single frame are applied.
|
||||||
// this avoids inadvertently changing the slider path type for batch operations.
|
// this avoids inadvertently changing the hit object path type for batch operations.
|
||||||
sliderVersion.BindValueChanged(_ => Scheduler.AddOnce(() =>
|
hitObjectVersion.BindValueChanged(_ => Scheduler.AddOnce(() =>
|
||||||
{
|
{
|
||||||
cachePoints(slider);
|
cachePoints(hitObject);
|
||||||
updatePathType();
|
updatePathType();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -120,11 +122,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
sliderPosition = slider.PositionBindable.GetBoundCopy();
|
hitObjectPosition = hitObject.PositionBindable.GetBoundCopy();
|
||||||
sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
|
hitObjectPosition.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
sliderScale = slider.ScaleBindable.GetBoundCopy();
|
hitObjectScale = hitObject.ScaleBindable.GetBoundCopy();
|
||||||
sliderScale.BindValueChanged(_ => updateMarkerDisplay());
|
hitObjectScale.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
|
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
@ -212,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
protected override void OnDragEnd(DragEndEvent e) => DragEnded?.Invoke();
|
protected override void OnDragEnd(DragEndEvent e) => DragEnded?.Invoke();
|
||||||
|
|
||||||
private void cachePoints(Slider slider) => PointsInSegment = slider.Path.PointsInSegment(ControlPoint);
|
private void cachePoints(T hitObject) => PointsInSegment = hitObject.Path.PointsInSegment(ControlPoint);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles correction of invalid path types.
|
/// Handles correction of invalid path types.
|
||||||
@ -239,7 +241,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void updateMarkerDisplay()
|
private void updateMarkerDisplay()
|
||||||
{
|
{
|
||||||
Position = slider.StackedPosition + ControlPoint.Position;
|
Position = hitObject.StackedPosition + ControlPoint.Position;
|
||||||
|
|
||||||
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
||||||
|
|
||||||
@ -249,7 +251,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
colour = colour.Lighten(1);
|
colour = colour.Lighten(1);
|
||||||
|
|
||||||
marker.Colour = colour;
|
marker.Colour = colour;
|
||||||
marker.Scale = new Vector2(slider.Scale);
|
marker.Scale = new Vector2(hitObject.Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 getColourFromNodeType()
|
private Color4 getColourFromNodeType()
|
||||||
|
@ -29,15 +29,16 @@ using osuTK.Input;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||||
{
|
{
|
||||||
public partial class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
public partial class PathControlPointVisualiser<T> : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
||||||
|
where T : OsuHitObject, IHasPath
|
||||||
{
|
{
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // allow context menu to appear outside of the playfield.
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // allow context menu to appear outside of the playfield.
|
||||||
|
|
||||||
internal readonly Container<PathControlPointPiece> Pieces;
|
internal readonly Container<PathControlPointPiece<T>> Pieces;
|
||||||
internal readonly Container<PathControlPointConnectionPiece> Connections;
|
internal readonly Container<PathControlPointConnectionPiece<T>> Connections;
|
||||||
|
|
||||||
private readonly IBindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
|
private readonly IBindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
|
||||||
private readonly Slider slider;
|
private readonly T hitObject;
|
||||||
private readonly bool allowSelection;
|
private readonly bool allowSelection;
|
||||||
|
|
||||||
private InputManager inputManager;
|
private InputManager inputManager;
|
||||||
@ -48,17 +49,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IDistanceSnapProvider snapProvider { get; set; }
|
private IDistanceSnapProvider snapProvider { get; set; }
|
||||||
|
|
||||||
public PathControlPointVisualiser(Slider slider, bool allowSelection)
|
public PathControlPointVisualiser(T hitObject, bool allowSelection)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
this.hitObject = hitObject;
|
||||||
this.allowSelection = allowSelection;
|
this.allowSelection = allowSelection;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
Connections = new Container<PathControlPointConnectionPiece> { RelativeSizeAxes = Axes.Both },
|
Connections = new Container<PathControlPointConnectionPiece<T>> { RelativeSizeAxes = Axes.Both },
|
||||||
Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both }
|
Pieces = new Container<PathControlPointPiece<T>> { RelativeSizeAxes = Axes.Both }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,12 +70,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
inputManager = GetContainingInputManager();
|
inputManager = GetContainingInputManager();
|
||||||
|
|
||||||
controlPoints.CollectionChanged += onControlPointsChanged;
|
controlPoints.CollectionChanged += onControlPointsChanged;
|
||||||
controlPoints.BindTo(slider.Path.ControlPoints);
|
controlPoints.BindTo(hitObject.Path.ControlPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects the <see cref="PathControlPointPiece"/> corresponding to the given <paramref name="pathControlPoint"/>,
|
/// Selects the <see cref="PathControlPointPiece{T}"/> corresponding to the given <paramref name="pathControlPoint"/>,
|
||||||
/// and deselects all other <see cref="PathControlPointPiece"/>s.
|
/// and deselects all other <see cref="PathControlPointPiece{T}"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetSelectionTo(PathControlPoint pathControlPoint)
|
public void SetSelectionTo(PathControlPoint pathControlPoint)
|
||||||
{
|
{
|
||||||
@ -124,8 +125,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool isSplittable(PathControlPointPiece p) =>
|
private bool isSplittable(PathControlPointPiece<T> p) =>
|
||||||
// A slider can only be split on control points which connect two different slider segments.
|
// A hit object can only be split on control points which connect two different path segments.
|
||||||
p.ControlPoint.Type.HasValue && p != Pieces.FirstOrDefault() && p != Pieces.LastOrDefault();
|
p.ControlPoint.Type.HasValue && p != Pieces.FirstOrDefault() && p != Pieces.LastOrDefault();
|
||||||
|
|
||||||
private void onControlPointsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
private void onControlPointsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
@ -150,7 +151,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
var point = (PathControlPoint)e.NewItems[i];
|
var point = (PathControlPoint)e.NewItems[i];
|
||||||
|
|
||||||
Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
|
Pieces.Add(new PathControlPointPiece<T>(hitObject, point).With(d =>
|
||||||
{
|
{
|
||||||
if (allowSelection)
|
if (allowSelection)
|
||||||
d.RequestSelection = selectionRequested;
|
d.RequestSelection = selectionRequested;
|
||||||
@ -160,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
d.DragEnded = dragEnded;
|
d.DragEnded = dragEnded;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Connections.Add(new PathControlPointConnectionPiece(slider, e.NewStartingIndex + i));
|
Connections.Add(new PathControlPointConnectionPiece<T>(hitObject, e.NewStartingIndex + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -219,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectionRequested(PathControlPointPiece piece, MouseButtonEvent e)
|
private void selectionRequested(PathControlPointPiece<T> piece, MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
||||||
piece.IsSelected.Toggle();
|
piece.IsSelected.Toggle();
|
||||||
@ -234,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="piece">The control point piece that we want to change the path type of.</param>
|
/// <param name="piece">The control point piece that we want to change the path type of.</param>
|
||||||
/// <param name="type">The path type we want to assign to the given control point piece.</param>
|
/// <param name="type">The path type we want to assign to the given control point piece.</param>
|
||||||
private void updatePathType(PathControlPointPiece piece, PathType? type)
|
private void updatePathType(PathControlPointPiece<T> piece, PathType? type)
|
||||||
{
|
{
|
||||||
int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint);
|
int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint);
|
||||||
|
|
||||||
@ -252,7 +253,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
slider.Path.ExpectedDistance.Value = null;
|
hitObject.Path.ExpectedDistance.Value = null;
|
||||||
piece.ControlPoint.Type = type;
|
piece.ControlPoint.Type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,9 +269,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
private void dragStarted(PathControlPoint controlPoint)
|
private void dragStarted(PathControlPoint controlPoint)
|
||||||
{
|
{
|
||||||
dragStartPositions = slider.Path.ControlPoints.Select(point => point.Position).ToArray();
|
dragStartPositions = hitObject.Path.ControlPoints.Select(point => point.Position).ToArray();
|
||||||
dragPathTypes = slider.Path.ControlPoints.Select(point => point.Type).ToArray();
|
dragPathTypes = hitObject.Path.ControlPoints.Select(point => point.Type).ToArray();
|
||||||
draggedControlPointIndex = slider.Path.ControlPoints.IndexOf(controlPoint);
|
draggedControlPointIndex = hitObject.Path.ControlPoints.IndexOf(controlPoint);
|
||||||
selectedControlPoints = new HashSet<PathControlPoint>(Pieces.Where(piece => piece.IsSelected.Value).Select(piece => piece.ControlPoint));
|
selectedControlPoints = new HashSet<PathControlPoint>(Pieces.Where(piece => piece.IsSelected.Value).Select(piece => piece.ControlPoint));
|
||||||
|
|
||||||
Debug.Assert(draggedControlPointIndex >= 0);
|
Debug.Assert(draggedControlPointIndex >= 0);
|
||||||
@ -280,25 +281,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
private void dragInProgress(DragEvent e)
|
private void dragInProgress(DragEvent e)
|
||||||
{
|
{
|
||||||
Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray();
|
Vector2[] oldControlPoints = hitObject.Path.ControlPoints.Select(cp => cp.Position).ToArray();
|
||||||
var oldPosition = slider.Position;
|
var oldPosition = hitObject.Position;
|
||||||
double oldStartTime = slider.StartTime;
|
double oldStartTime = hitObject.StartTime;
|
||||||
|
|
||||||
if (selectedControlPoints.Contains(slider.Path.ControlPoints[0]))
|
if (selectedControlPoints.Contains(hitObject.Path.ControlPoints[0]))
|
||||||
{
|
{
|
||||||
// Special handling for selections containing head control point - the position of the slider changes which means the snapped position and time have to be taken into account
|
// Special handling for selections containing head control point - the position of the hit object changes which means the snapped position and time have to be taken into account
|
||||||
Vector2 newHeadPosition = Parent.ToScreenSpace(e.MousePosition + (dragStartPositions[0] - dragStartPositions[draggedControlPointIndex]));
|
Vector2 newHeadPosition = Parent.ToScreenSpace(e.MousePosition + (dragStartPositions[0] - dragStartPositions[draggedControlPointIndex]));
|
||||||
var result = snapProvider?.FindSnappedPositionAndTime(newHeadPosition);
|
var result = snapProvider?.FindSnappedPositionAndTime(newHeadPosition);
|
||||||
|
|
||||||
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? newHeadPosition) - slider.Position;
|
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? newHeadPosition) - hitObject.Position;
|
||||||
|
|
||||||
slider.Position += movementDelta;
|
hitObject.Position += movementDelta;
|
||||||
slider.StartTime = result?.Time ?? slider.StartTime;
|
hitObject.StartTime = result?.Time ?? hitObject.StartTime;
|
||||||
|
|
||||||
for (int i = 1; i < slider.Path.ControlPoints.Count; i++)
|
for (int i = 1; i < hitObject.Path.ControlPoints.Count; i++)
|
||||||
{
|
{
|
||||||
var controlPoint = slider.Path.ControlPoints[i];
|
var controlPoint = hitObject.Path.ControlPoints[i];
|
||||||
// Since control points are relative to the position of the slider, all points that are _not_ selected
|
// Since control points are relative to the position of the hit object, all points that are _not_ selected
|
||||||
// need to be offset _back_ by the delta corresponding to the movement of the head point.
|
// need to be offset _back_ by the delta corresponding to the movement of the head point.
|
||||||
// All other selected control points (if any) will move together with the head point
|
// All other selected control points (if any) will move together with the head point
|
||||||
// (and so they will not move at all, relative to each other).
|
// (and so they will not move at all, relative to each other).
|
||||||
@ -310,7 +311,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
var result = snapProvider?.FindSnappedPositionAndTime(Parent.ToScreenSpace(e.MousePosition));
|
var result = snapProvider?.FindSnappedPositionAndTime(Parent.ToScreenSpace(e.MousePosition));
|
||||||
|
|
||||||
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? Parent.ToScreenSpace(e.MousePosition)) - dragStartPositions[draggedControlPointIndex] - slider.Position;
|
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? Parent.ToScreenSpace(e.MousePosition)) - dragStartPositions[draggedControlPointIndex] - hitObject.Position;
|
||||||
|
|
||||||
for (int i = 0; i < controlPoints.Count; ++i)
|
for (int i = 0; i < controlPoints.Count; ++i)
|
||||||
{
|
{
|
||||||
@ -321,23 +322,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Snap the path to the current beat divisor before checking length validity.
|
// Snap the path to the current beat divisor before checking length validity.
|
||||||
slider.SnapTo(snapProvider);
|
hitObject.SnapTo(snapProvider);
|
||||||
|
|
||||||
if (!slider.Path.HasValidLength)
|
if (!hitObject.Path.HasValidLength)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
for (int i = 0; i < hitObject.Path.ControlPoints.Count; i++)
|
||||||
slider.Path.ControlPoints[i].Position = oldControlPoints[i];
|
hitObject.Path.ControlPoints[i].Position = oldControlPoints[i];
|
||||||
|
|
||||||
slider.Position = oldPosition;
|
hitObject.Position = oldPosition;
|
||||||
slider.StartTime = oldStartTime;
|
hitObject.StartTime = oldStartTime;
|
||||||
// Snap the path length again to undo the invalid length.
|
// Snap the path length again to undo the invalid length.
|
||||||
slider.SnapTo(snapProvider);
|
hitObject.SnapTo(snapProvider);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maintain the path types in case they got defaulted to bezier at some point during the drag.
|
// Maintain the path types in case they got defaulted to bezier at some point during the drag.
|
||||||
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
for (int i = 0; i < hitObject.Path.ControlPoints.Count; i++)
|
||||||
slider.Path.ControlPoints[i].Type = dragPathTypes[i];
|
hitObject.Path.ControlPoints[i].Type = dragPathTypes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dragEnded() => changeHandler?.EndChange();
|
private void dragEnded() => changeHandler?.EndChange();
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
private SliderBodyPiece bodyPiece;
|
private SliderBodyPiece bodyPiece;
|
||||||
private HitCirclePiece headCirclePiece;
|
private HitCirclePiece headCirclePiece;
|
||||||
private HitCirclePiece tailCirclePiece;
|
private HitCirclePiece tailCirclePiece;
|
||||||
private PathControlPointVisualiser controlPointVisualiser;
|
private PathControlPointVisualiser<Slider> controlPointVisualiser;
|
||||||
|
|
||||||
private InputManager inputManager;
|
private InputManager inputManager;
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
bodyPiece = new SliderBodyPiece(),
|
bodyPiece = new SliderBodyPiece(),
|
||||||
headCirclePiece = new HitCirclePiece(),
|
headCirclePiece = new HitCirclePiece(),
|
||||||
tailCirclePiece = new HitCirclePiece(),
|
tailCirclePiece = new HitCirclePiece(),
|
||||||
controlPointVisualiser = new PathControlPointVisualiser(HitObject, false)
|
controlPointVisualiser = new PathControlPointVisualiser<Slider>(HitObject, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
setState(SliderPlacementState.Initial);
|
setState(SliderPlacementState.Initial);
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
protected SliderCircleOverlay TailOverlay { get; private set; }
|
protected SliderCircleOverlay TailOverlay { get; private set; }
|
||||||
|
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
protected PathControlPointVisualiser ControlPointVisualiser { get; private set; }
|
protected PathControlPointVisualiser<Slider> ControlPointVisualiser { get; private set; }
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IDistanceSnapProvider snapProvider { get; set; }
|
private IDistanceSnapProvider snapProvider { get; set; }
|
||||||
@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
{
|
{
|
||||||
if (ControlPointVisualiser == null)
|
if (ControlPointVisualiser == null)
|
||||||
{
|
{
|
||||||
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(HitObject, true)
|
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser<Slider>(HitObject, true)
|
||||||
{
|
{
|
||||||
RemoveControlPointsRequested = removeControlPoints,
|
RemoveControlPointsRequested = removeControlPoints,
|
||||||
SplitControlPointsRequested = splitControlPoints
|
SplitControlPointsRequested = splitControlPoints
|
||||||
|
@ -187,28 +187,19 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
if (b.IsSelected)
|
if (b.IsSelected)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var hitObject = (OsuHitObject)b.Item;
|
var snapPositions = b.ScreenSpaceSnapPoints;
|
||||||
|
|
||||||
Vector2? snap = checkSnap(hitObject.Position);
|
if (!snapPositions.Any())
|
||||||
if (snap == null && hitObject.Position != hitObject.EndPosition)
|
continue;
|
||||||
snap = checkSnap(hitObject.EndPosition);
|
|
||||||
|
|
||||||
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
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2? checkSnap(Vector2 checkPos)
|
|
||||||
{
|
|
||||||
Vector2 checkScreenPos = playfield.GamefieldToScreenSpace(checkPos);
|
|
||||||
|
|
||||||
if (Vector2.Distance(checkScreenPos, screenSpacePosition) < snapRadius)
|
|
||||||
return checkScreenPos;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
snapResult = null;
|
snapResult = null;
|
||||||
|
@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
PathVersion.UnbindFrom(HitObject.Path.Version);
|
PathVersion.UnbindFrom(HitObject.Path.Version);
|
||||||
|
|
||||||
slidingSample.Samples = null;
|
slidingSample?.ClearSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadSamples()
|
protected override void LoadSamples()
|
||||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.OnFree();
|
base.OnFree();
|
||||||
|
|
||||||
spinningSample.Samples = null;
|
spinningSample.ClearSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadSamples()
|
protected override void LoadSamples()
|
||||||
|
@ -9,8 +9,10 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu
|
namespace osu.Game.Rulesets.Osu
|
||||||
{
|
{
|
||||||
@ -40,6 +42,13 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
protected override KeyBindingContainer<OsuAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
protected override KeyBindingContainer<OsuAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||||
=> new OsuKeyBindingContainer(ruleset, variant, unique);
|
=> new OsuKeyBindingContainer(ruleset, variant, unique);
|
||||||
|
|
||||||
|
public bool CheckScreenSpaceActionPressJudgeable(Vector2 screenSpacePosition) =>
|
||||||
|
// This is a very naive but simple approach.
|
||||||
|
//
|
||||||
|
// Based on user feedback of more nuanced scenarios (where touch doesn't behave as expected),
|
||||||
|
// this can be expanded to a more complex implementation, but I'd still want to keep it as simple as we can.
|
||||||
|
NonPositionalInputQueue.OfType<DrawableHitCircle.HitReceptor>().Any(c => c.ReceivePositionalInputAt(screenSpacePosition));
|
||||||
|
|
||||||
public OsuInputManager(RulesetInfo ruleset)
|
public OsuInputManager(RulesetInfo ruleset)
|
||||||
: base(ruleset, 0, SimultaneousBindingMode.Unique)
|
: base(ruleset, 0, SimultaneousBindingMode.Unique)
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Colour;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -43,6 +44,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||||
private readonly FlashPiece flash;
|
private readonly FlashPiece flash;
|
||||||
|
private readonly Container kiaiContainer;
|
||||||
|
|
||||||
|
private Bindable<bool> configHitLighting = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private DrawableHitObject drawableObject { get; set; } = null!;
|
private DrawableHitObject drawableObject { get; set; } = null!;
|
||||||
@ -64,24 +68,32 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
outerGradient = new Circle // renders the outer bright gradient
|
outerGradient = new Circle // renders the outer bright gradient
|
||||||
{
|
{
|
||||||
Size = new Vector2(OUTER_GRADIENT_SIZE),
|
Size = new Vector2(OUTER_GRADIENT_SIZE),
|
||||||
Alpha = 1,
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
innerGradient = new Circle // renders the inner bright gradient
|
innerGradient = new Circle // renders the inner bright gradient
|
||||||
{
|
{
|
||||||
Size = new Vector2(INNER_GRADIENT_SIZE),
|
Size = new Vector2(INNER_GRADIENT_SIZE),
|
||||||
Alpha = 1,
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
innerFill = new Circle // renders the inner dark fill
|
innerFill = new Circle // renders the inner dark fill
|
||||||
{
|
{
|
||||||
Size = new Vector2(INNER_FILL_SIZE),
|
Size = new Vector2(INNER_FILL_SIZE),
|
||||||
Alpha = 1,
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = 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
|
number = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Font = OsuFont.Default.With(size: 52, weight: FontWeight.Bold),
|
Font = OsuFont.Default.With(size: 52, weight: FontWeight.Bold),
|
||||||
@ -96,12 +108,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
var drawableOsuObject = (DrawableOsuHitObject)drawableObject;
|
var drawableOsuObject = (DrawableOsuHitObject)drawableObject;
|
||||||
|
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||||
|
|
||||||
|
configHitLighting = config.GetBindable<bool>(OsuSetting.HitLighting);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -117,20 +131,25 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
outerGradient.ClearTransforms(targetMember: nameof(Colour));
|
outerGradient.ClearTransforms(targetMember: nameof(Colour));
|
||||||
outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f));
|
outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f));
|
||||||
|
|
||||||
|
kiaiContainer.Colour = colour.NewValue;
|
||||||
outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4);
|
outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4);
|
||||||
innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f));
|
innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f));
|
||||||
flash.Colour = colour.NewValue;
|
flash.Colour = colour.NewValue;
|
||||||
|
|
||||||
// Accent colour may be changed many times during a paused gameplay state.
|
// Accent colour may be changed many times during a paused gameplay state.
|
||||||
// Schedule the change to avoid transforms piling up.
|
// Schedule the change to avoid transforms piling up.
|
||||||
Scheduler.AddOnce(updateStateTransforms);
|
Scheduler.AddOnce(() =>
|
||||||
|
{
|
||||||
|
ApplyTransformsAt(double.MinValue, true);
|
||||||
|
ClearTransformsAfter(double.MinValue, true);
|
||||||
|
|
||||||
|
updateStateTransforms(drawableObject, drawableObject.State.Value);
|
||||||
|
});
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
|
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStateTransforms() => updateStateTransforms(drawableObject, drawableObject.State.Value);
|
|
||||||
|
|
||||||
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||||
@ -140,7 +159,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
// Fade out time is at a maximum of 800. Must match `DrawableHitCircle`'s arbitrary lifetime spec.
|
// Fade out time is at a maximum of 800. Must match `DrawableHitCircle`'s arbitrary lifetime spec.
|
||||||
const double fade_out_time = 800;
|
const double fade_out_time = 800;
|
||||||
|
|
||||||
const double flash_in_duration = 150;
|
const double flash_in_duration = 150;
|
||||||
const double resize_duration = 400;
|
const double resize_duration = 400;
|
||||||
|
|
||||||
@ -171,20 +189,40 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
// gradient layers.
|
// gradient layers.
|
||||||
border.ResizeTo(Size * shrink_size + new Vector2(border.BorderThickness), resize_duration, Easing.OutElasticHalf);
|
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.
|
// 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.
|
// This is to give it a bomb-like effect, with the border "triggering" its animation when getting close.
|
||||||
using (BeginDelayedSequence(flash_in_duration / 12))
|
using (BeginDelayedSequence(flash_in_duration / 12))
|
||||||
{
|
{
|
||||||
outerGradient.ResizeTo(OUTER_GRADIENT_SIZE * shrink_size, resize_duration, Easing.OutElasticHalf);
|
outerGradient.ResizeTo(OUTER_GRADIENT_SIZE * shrink_size, resize_duration, Easing.OutElasticHalf);
|
||||||
|
|
||||||
outerGradient
|
outerGradient
|
||||||
.FadeColour(Color4.White, 80)
|
.FadeColour(Color4.White, 80)
|
||||||
.Then()
|
.Then()
|
||||||
.FadeOut(flash_in_duration);
|
.FadeOut(flash_in_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
flash.FadeTo(1, flash_in_duration, Easing.OutQuint);
|
if (configHitLighting.Value)
|
||||||
|
{
|
||||||
|
flash.HitLighting = true;
|
||||||
|
flash.FadeTo(1, flash_in_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
this.FadeOut(fade_out_time, Easing.OutQuad);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,6 +253,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
Child.AlwaysPresent = true;
|
Child.AlwaysPresent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HitLighting { get; set; }
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -223,7 +263,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
Colour = Colour,
|
Colour = Colour,
|
||||||
Radius = OsuHitObject.OBJECT_RADIUS * 1.2f,
|
Radius = OsuHitObject.OBJECT_RADIUS * (HitLighting ? 1.2f : 0.6f),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly List<TrackedTouch> trackedTouches = new List<TrackedTouch>();
|
private readonly List<TrackedTouch> trackedTouches = new List<TrackedTouch>();
|
||||||
|
|
||||||
|
private TrackedTouch? positionTrackingTouch;
|
||||||
|
|
||||||
private readonly OsuInputManager osuInputManager;
|
private readonly OsuInputManager osuInputManager;
|
||||||
|
|
||||||
private Bindable<bool> mouseDisabled = null!;
|
private Bindable<bool> mouseDisabled = null!;
|
||||||
@ -57,7 +59,15 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
// Ignore any taps which trigger an action which is already handled. But track them for potential positional input in the future.
|
// Ignore any taps which trigger an action which is already handled. But track them for potential positional input in the future.
|
||||||
bool shouldResultInAction = osuInputManager.AllowGameplayInputs && !mouseDisabled.Value && trackedTouches.All(t => t.Action != action);
|
bool shouldResultInAction = osuInputManager.AllowGameplayInputs && !mouseDisabled.Value && trackedTouches.All(t => t.Action != action);
|
||||||
|
|
||||||
trackedTouches.Add(new TrackedTouch(e.Touch.Source, shouldResultInAction ? action : null));
|
// If we can actually accept as an action, check whether this tap was on a circle's receptor.
|
||||||
|
// This case gets special handling to allow for empty-space stream tapping.
|
||||||
|
bool isDirectCircleTouch = osuInputManager.CheckScreenSpaceActionPressJudgeable(e.ScreenSpaceTouchDownPosition);
|
||||||
|
|
||||||
|
var newTouch = new TrackedTouch(e.Touch.Source, shouldResultInAction ? action : null, isDirectCircleTouch);
|
||||||
|
|
||||||
|
updatePositionTracking(newTouch);
|
||||||
|
|
||||||
|
trackedTouches.Add(newTouch);
|
||||||
|
|
||||||
// Important to update position before triggering the pressed action.
|
// Important to update position before triggering the pressed action.
|
||||||
handleTouchMovement(e);
|
handleTouchMovement(e);
|
||||||
@ -68,10 +78,47 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a new touch, update the positional tracking state and any related operations.
|
||||||
|
/// </summary>
|
||||||
|
private void updatePositionTracking(TrackedTouch newTouch)
|
||||||
|
{
|
||||||
|
// If the new touch directly interacted with a circle's receptor, it always becomes the current touch for positional tracking.
|
||||||
|
if (newTouch.DirectTouch)
|
||||||
|
{
|
||||||
|
positionTrackingTouch = newTouch;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we only want to use the new touch for position tracking if no other touch is tracking position yet..
|
||||||
|
if (positionTrackingTouch == null)
|
||||||
|
{
|
||||||
|
positionTrackingTouch = newTouch;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ..or if the current position tracking touch was not a direct touch (this one is debatable and may be change in the future, but it's the simplest way to handle)
|
||||||
|
if (!positionTrackingTouch.DirectTouch)
|
||||||
|
{
|
||||||
|
positionTrackingTouch = newTouch;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the case the new touch was not used for position tracking, we should also check the previous position tracking touch.
|
||||||
|
// If it was a direct touch and still has its action pressed, that action should be released.
|
||||||
|
//
|
||||||
|
// This is done to allow tracking with the initial touch while still having both Left/Right actions available for alternating with two more touches.
|
||||||
|
if (positionTrackingTouch.DirectTouch && positionTrackingTouch.Action is OsuAction directTouchAction)
|
||||||
|
{
|
||||||
|
osuInputManager.KeyBindingContainer.TriggerReleased(directTouchAction);
|
||||||
|
positionTrackingTouch.Action = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleTouchMovement(TouchEvent touchEvent)
|
private void handleTouchMovement(TouchEvent touchEvent)
|
||||||
{
|
{
|
||||||
// Movement should only be tracked for the most recent touch.
|
// Movement should only be tracked for the most recent touch.
|
||||||
if (touchEvent.Touch.Source != trackedTouches.Last().Source)
|
if (touchEvent.Touch.Source != positionTrackingTouch?.Source)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!osuInputManager.AllowUserCursorMovement)
|
if (!osuInputManager.AllowUserCursorMovement)
|
||||||
@ -87,6 +134,9 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
if (tracked.Action is OsuAction action)
|
if (tracked.Action is OsuAction action)
|
||||||
osuInputManager.KeyBindingContainer.TriggerReleased(action);
|
osuInputManager.KeyBindingContainer.TriggerReleased(action);
|
||||||
|
|
||||||
|
if (positionTrackingTouch == tracked)
|
||||||
|
positionTrackingTouch = null;
|
||||||
|
|
||||||
trackedTouches.Remove(tracked);
|
trackedTouches.Remove(tracked);
|
||||||
|
|
||||||
base.OnTouchUp(e);
|
base.OnTouchUp(e);
|
||||||
@ -96,12 +146,15 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
public readonly TouchSource Source;
|
public readonly TouchSource Source;
|
||||||
|
|
||||||
public readonly OsuAction? Action;
|
public OsuAction? Action;
|
||||||
|
|
||||||
public TrackedTouch(TouchSource source, OsuAction? action)
|
public readonly bool DirectTouch;
|
||||||
|
|
||||||
|
public TrackedTouch(TouchSource source, OsuAction? action, bool directTouch)
|
||||||
{
|
{
|
||||||
Source = source;
|
Source = source;
|
||||||
Action = action;
|
Action = action;
|
||||||
|
DirectTouch = directTouch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
[TestCase("slider-conversion-v6")]
|
[TestCase("slider-conversion-v6")]
|
||||||
[TestCase("slider-conversion-v14")]
|
[TestCase("slider-conversion-v14")]
|
||||||
[TestCase("slider-generating-drumroll-2")]
|
[TestCase("slider-generating-drumroll-2")]
|
||||||
|
[TestCase("file-hitsamples")]
|
||||||
public void Test(string name) => base.Test(name);
|
public void Test(string name) => base.Test(name);
|
||||||
|
|
||||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
using osu.Game.Rulesets.Taiko.Configuration;
|
||||||
using osu.Game.Rulesets.Taiko.UI;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
@ -14,36 +17,48 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
private DrumTouchInputArea drumTouchInputArea = null!;
|
private DrumTouchInputArea drumTouchInputArea = null!;
|
||||||
|
|
||||||
[SetUpSteps]
|
private readonly Bindable<TaikoTouchControlScheme> controlScheme = new Bindable<TaikoTouchControlScheme>();
|
||||||
public void SetUpSteps()
|
|
||||||
|
[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,
|
new InputDrum
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new InputDrum
|
Anchor = Anchor.TopCentre,
|
||||||
{
|
Origin = Anchor.TopCentre,
|
||||||
Anchor = Anchor.TopCentre,
|
Height = 0.2f,
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
Height = 0.2f,
|
|
||||||
},
|
|
||||||
drumTouchInputArea = new DrumTouchInputArea
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
drumTouchInputArea = new DrumTouchInputArea
|
||||||
});
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDrum()
|
public void TestDrum()
|
||||||
{
|
{
|
||||||
|
AddStep("create drum", createDrum);
|
||||||
AddStep("show drum", () => drumTouchInputArea.Show());
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -2,13 +2,15 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.UI;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Mods
|
namespace osu.Game.Rulesets.Taiko.Mods
|
||||||
{
|
{
|
||||||
public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset<TaikoHitObject>
|
public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset<TaikoHitObject>, IApplicableToDrawableHitObject
|
||||||
{
|
{
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
@ -18,5 +20,11 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
|
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
|
||||||
playfield.ClassicHitTargetPosition.Value = true;
|
playfield.ClassicHitTargetPosition.Value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ApplyToDrawableHitObject(DrawableHitObject drawable)
|
||||||
|
{
|
||||||
|
if (drawable is DrawableTaikoHitObject hit)
|
||||||
|
hit.SnapJudgementLocation = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,6 +207,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
const float gravity_time = 300;
|
const float gravity_time = 300;
|
||||||
const float gravity_travel_height = 200;
|
const float gravity_travel_height = 200;
|
||||||
|
|
||||||
|
if (SnapJudgementLocation)
|
||||||
|
MainPiece.MoveToX(-X);
|
||||||
|
|
||||||
this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
|
this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
|
||||||
|
|
||||||
this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out)
|
this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out)
|
||||||
|
@ -25,6 +25,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
private readonly Container nonProxiedContent;
|
private readonly Container nonProxiedContent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the location of the hit should be snapped to the hit target before animating.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is how osu-stable worked, but notably is not how TnT works.
|
||||||
|
/// Not snapping results in less visual feedback on hit accuracy.
|
||||||
|
/// </remarks>
|
||||||
|
public bool SnapJudgementLocation { get; set; }
|
||||||
|
|
||||||
protected DrawableTaikoHitObject([CanBeNull] TaikoHitObject hitObject)
|
protected DrawableTaikoHitObject([CanBeNull] TaikoHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
{"Mappings":[{"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"IsRim":false,"IsCentre":true,"IsDrumRoll":false,"IsSwell":false,"IsStrong":false}]},{"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":false}]},{"StartTime":1500.0,"Objects":[{"StartTime":1500.0,"EndTime":1500.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":false}]},{"StartTime":2000.0,"Objects":[{"StartTime":2000.0,"EndTime":2000.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":false}]},{"StartTime":2500.0,"Objects":[{"StartTime":2500.0,"EndTime":2500.0,"IsRim":false,"IsCentre":true,"IsDrumRoll":false,"IsSwell":false,"IsStrong":true}]},{"StartTime":3000.0,"Objects":[{"StartTime":3000.0,"EndTime":3000.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":true}]},{"StartTime":3500.0,"Objects":[{"StartTime":3500.0,"EndTime":3500.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":true}]},{"StartTime":4000.0,"Objects":[{"StartTime":4000.0,"EndTime":4000.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":true}]}]}
|
@ -0,0 +1,22 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:5
|
||||||
|
CircleSize:7
|
||||||
|
OverallDifficulty:6.5
|
||||||
|
ApproachRate:10
|
||||||
|
SliderMultiplier:1.9
|
||||||
|
SliderTickRate:1
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
500,500,4,2,1,50,1,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
256,192,500,1,0,0:0:0:0:sample.ogg
|
||||||
|
256,192,1000,1,8,0:0:0:0:sample.ogg
|
||||||
|
256,192,1500,1,2,0:0:0:0:sample.ogg
|
||||||
|
256,192,2000,1,10,0:0:0:0:sample.ogg
|
||||||
|
256,192,2500,1,4,0:0:0:0:sample.ogg
|
||||||
|
256,192,3000,1,12,0:0:0:0:sample.ogg
|
||||||
|
256,192,3500,1,6,0:0:0:0:sample.ogg
|
||||||
|
256,192,4000,1,14,0:0:0:0:sample.ogg
|
@ -28,9 +28,13 @@ using osu.Game.Rulesets.Taiko.Skinning.Argon;
|
|||||||
using osu.Game.Rulesets.Taiko.Skinning.Legacy;
|
using osu.Game.Rulesets.Taiko.Skinning.Legacy;
|
||||||
using osu.Game.Rulesets.Taiko.UI;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Ranking.Statistics;
|
using osu.Game.Screens.Ranking.Statistics;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Rulesets.Configuration;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Taiko.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko
|
namespace osu.Game.Rulesets.Taiko
|
||||||
{
|
{
|
||||||
@ -194,6 +198,10 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
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()
|
protected override IEnumerable<HitResult> GetValidHitResults()
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
@ -201,9 +209,8 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
HitResult.Great,
|
HitResult.Great,
|
||||||
HitResult.Ok,
|
HitResult.Ok,
|
||||||
|
|
||||||
HitResult.SmallTickHit,
|
|
||||||
|
|
||||||
HitResult.SmallBonus,
|
HitResult.SmallBonus,
|
||||||
|
HitResult.LargeBonus,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +219,9 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
case HitResult.SmallBonus:
|
case HitResult.SmallBonus:
|
||||||
|
return "drum tick";
|
||||||
|
|
||||||
|
case HitResult.LargeBonus:
|
||||||
return "bonus";
|
return "bonus";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -11,6 +13,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Taiko.Configuration;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -31,15 +34,18 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
private Container mainContent = null!;
|
private Container mainContent = null!;
|
||||||
|
|
||||||
private QuarterCircle leftCentre = null!;
|
private DrumSegment leftCentre = null!;
|
||||||
private QuarterCircle rightCentre = null!;
|
private DrumSegment rightCentre = null!;
|
||||||
private QuarterCircle leftRim = null!;
|
private DrumSegment leftRim = null!;
|
||||||
private QuarterCircle rightRim = null!;
|
private DrumSegment rightRim = null!;
|
||||||
|
|
||||||
|
private readonly Bindable<TaikoTouchControlScheme> configTouchControlScheme = new Bindable<TaikoTouchControlScheme>();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(TaikoInputManager taikoInputManager, OsuColour colours)
|
private void load(TaikoInputManager taikoInputManager, TaikoRulesetConfigManager config)
|
||||||
{
|
{
|
||||||
Debug.Assert(taikoInputManager.KeyBindingContainer != null);
|
Debug.Assert(taikoInputManager.KeyBindingContainer != null);
|
||||||
|
|
||||||
keyBindingContainer = taikoInputManager.KeyBindingContainer;
|
keyBindingContainer = taikoInputManager.KeyBindingContainer;
|
||||||
|
|
||||||
// Container should handle input everywhere.
|
// Container should handle input everywhere.
|
||||||
@ -65,27 +71,27 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
leftRim = new QuarterCircle(TaikoAction.LeftRim, colours.Blue)
|
leftRim = new DrumSegment
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
X = -2,
|
X = -2,
|
||||||
},
|
},
|
||||||
rightRim = new QuarterCircle(TaikoAction.RightRim, colours.Blue)
|
rightRim = new DrumSegment
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
X = 2,
|
X = 2,
|
||||||
Rotation = 90,
|
Rotation = 90,
|
||||||
},
|
},
|
||||||
leftCentre = new QuarterCircle(TaikoAction.LeftCentre, colours.Pink)
|
leftCentre = new DrumSegment
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
X = -2,
|
X = -2,
|
||||||
Scale = new Vector2(centre_region),
|
Scale = new Vector2(centre_region),
|
||||||
},
|
},
|
||||||
rightCentre = new QuarterCircle(TaikoAction.RightCentre, colours.Pink)
|
rightCentre = new DrumSegment
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
Origin = Anchor.BottomRight,
|
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)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
@ -119,11 +136,47 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
base.OnTouchUp(e);
|
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)
|
private void handleDown(object source, Vector2 position)
|
||||||
{
|
{
|
||||||
Show();
|
Show();
|
||||||
|
|
||||||
TaikoAction taikoAction = getTaikoActionFromInput(position);
|
TaikoAction taikoAction = getTaikoActionFromPosition(position);
|
||||||
|
|
||||||
// Not too sure how this can happen, but let's avoid throwing.
|
// Not too sure how this can happen, but let's avoid throwing.
|
||||||
if (trackedActions.ContainsKey(source))
|
if (trackedActions.ContainsKey(source))
|
||||||
@ -139,18 +192,15 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
trackedActions.Remove(source);
|
trackedActions.Remove(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool validMouse(MouseButtonEvent e) =>
|
private TaikoAction getTaikoActionFromPosition(Vector2 inputPosition)
|
||||||
leftRim.Contains(e.ScreenSpaceMouseDownPosition) || rightRim.Contains(e.ScreenSpaceMouseDownPosition);
|
|
||||||
|
|
||||||
private TaikoAction getTaikoActionFromInput(Vector2 inputPosition)
|
|
||||||
{
|
{
|
||||||
bool centreHit = leftCentre.Contains(inputPosition) || rightCentre.Contains(inputPosition);
|
bool centreHit = leftCentre.Contains(inputPosition) || rightCentre.Contains(inputPosition);
|
||||||
bool leftSide = ToLocalSpace(inputPosition).X < DrawWidth / 2;
|
bool leftSide = ToLocalSpace(inputPosition).X < DrawWidth / 2;
|
||||||
|
|
||||||
if (leftSide)
|
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()
|
protected override void PopIn()
|
||||||
@ -163,23 +213,42 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
mainContent.FadeOut(300);
|
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 override bool Contains(Vector2 screenSpacePos) => circle.Contains(screenSpacePos);
|
||||||
|
|
||||||
public QuarterCircle(TaikoAction handledAction, Color4 colour)
|
public DrumSegment()
|
||||||
{
|
{
|
||||||
this.handledAction = handledAction;
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
FillMode = FillMode.Fit;
|
FillMode = FillMode.Fit;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
@ -191,7 +260,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
circle = new Circle
|
circle = new Circle
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = colour.Multiply(1.4f).Darken(2.8f),
|
|
||||||
Alpha = 0.8f,
|
Alpha = 0.8f,
|
||||||
Scale = new Vector2(2),
|
Scale = new Vector2(2),
|
||||||
},
|
},
|
||||||
@ -200,7 +268,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
Colour = colour,
|
|
||||||
Scale = new Vector2(2),
|
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)
|
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||||
{
|
{
|
||||||
if (e.Action == handledAction)
|
if (e.Action == Action)
|
||||||
overlay.FadeTo(1f, 80, Easing.OutQuint);
|
overlay.FadeTo(1f, 80, Easing.OutQuint);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
||||||
{
|
{
|
||||||
if (e.Action == handledAction)
|
if (e.Action == Action)
|
||||||
overlay.FadeOut(1000, Easing.OutQuint);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.That(oneTime.EndTime, Is.EqualTo(4000 + loop_duration));
|
Assert.That(oneTime.EndTime, Is.EqualTo(4000 + loop_duration));
|
||||||
|
|
||||||
StoryboardSprite manyTimes = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "many-times.png");
|
StoryboardSprite manyTimes = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "many-times.png");
|
||||||
Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + 40 * loop_duration));
|
// It is intentional that we don't consider the loop count (40) as part of the end time calculation to match stable's handling.
|
||||||
|
// If we were to include the loop count, storyboards which loop for stupid long loop counts would continue playing the outro forever.
|
||||||
|
Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + loop_duration));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
@ -12,7 +10,7 @@ using osu.Game.Screens.Edit;
|
|||||||
namespace osu.Game.Tests.Editing
|
namespace osu.Game.Tests.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class EditorChangeHandlerTest
|
public class BeatmapEditorChangeHandlerTest
|
||||||
{
|
{
|
||||||
private int stateChangedFired;
|
private int stateChangedFired;
|
||||||
|
|
||||||
@ -23,18 +21,23 @@ namespace osu.Game.Tests.Editing
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSaveRestoreState()
|
public void TestSaveRestoreStateUsingTransaction()
|
||||||
{
|
{
|
||||||
var (handler, beatmap) = createChangeHandler();
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
handler.BeginChange();
|
||||||
handler.SaveState();
|
|
||||||
|
|
||||||
|
// Initial state will be saved on BeginChange
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(1));
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
|
handler.EndChange();
|
||||||
|
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
@ -43,7 +46,35 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.True);
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
|
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSaveRestoreState()
|
||||||
|
{
|
||||||
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
// Save initial state
|
||||||
|
handler.SaveState();
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
|
handler.SaveState();
|
||||||
|
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(2));
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
|
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -54,6 +85,10 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
// Save initial state
|
||||||
|
handler.SaveState();
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
string originalHash = handler.CurrentStateHash;
|
string originalHash = handler.CurrentStateHash;
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
@ -61,7 +96,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(1));
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
string hash = handler.CurrentStateHash;
|
string hash = handler.CurrentStateHash;
|
||||||
|
|
||||||
@ -69,7 +104,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
handler.RestoreState(-1);
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
Assert.That(originalHash, Is.EqualTo(handler.CurrentStateHash));
|
Assert.That(originalHash, Is.EqualTo(handler.CurrentStateHash));
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(2));
|
Assert.That(stateChangedFired, Is.EqualTo(3));
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
@ -84,12 +119,16 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
// Save initial state
|
||||||
|
handler.SaveState();
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(1));
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
string hash = handler.CurrentStateHash;
|
string hash = handler.CurrentStateHash;
|
||||||
|
|
||||||
@ -97,7 +136,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
|
||||||
Assert.That(hash, Is.EqualTo(handler.CurrentStateHash));
|
Assert.That(hash, Is.EqualTo(handler.CurrentStateHash));
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(1));
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
handler.RestoreState(-1);
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
@ -106,7 +145,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
// we should only be able to restore once even though we saved twice.
|
// we should only be able to restore once even though we saved twice.
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.True);
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(2));
|
Assert.That(stateChangedFired, Is.EqualTo(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -114,11 +153,15 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var (handler, beatmap) = createChangeHandler();
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
|
// Save initial state
|
||||||
|
handler.SaveState();
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
|
||||||
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
||||||
{
|
{
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(i));
|
Assert.That(stateChangedFired, Is.EqualTo(i + 1));
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
@ -169,7 +212,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var changeHandler = new EditorChangeHandler(beatmap);
|
var changeHandler = new BeatmapEditorChangeHandler(beatmap);
|
||||||
|
|
||||||
changeHandler.OnStateChange += () => stateChangedFired++;
|
changeHandler.OnStateChange += () => stateChangedFired++;
|
||||||
return (changeHandler, beatmap);
|
return (changeHandler, beatmap);
|
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 IBindable<double> AggregateTempo => throw new NotImplementedException();
|
||||||
|
|
||||||
public int PlaybackConcurrency { get; set; }
|
public int PlaybackConcurrency { get; set; }
|
||||||
|
|
||||||
|
public void AddExtension(string extension) => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestShaderManager : ShaderManager
|
private class TestShaderManager : ShaderManager
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -20,29 +19,36 @@ namespace osu.Game.Tests.Skins
|
|||||||
public partial class TestSceneBeatmapSkinResources : OsuTestScene
|
public partial class TestSceneBeatmapSkinResources : OsuTestScene
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapManager beatmaps { get; set; }
|
private BeatmapManager beatmaps { get; set; } = null!;
|
||||||
|
|
||||||
private IWorkingBeatmap beatmap;
|
[Test]
|
||||||
|
public void TestRetrieveOggAudio()
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
{
|
||||||
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]
|
[Test]
|
||||||
public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo("sample")) != null);
|
public void TestRetrievalWithConflictingFilenames()
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestRetrieveOggTrack() => AddAssert("track is non-null", () =>
|
|
||||||
{
|
{
|
||||||
using (var track = beatmap.LoadTrack())
|
IWorkingBeatmap beatmap = null!;
|
||||||
return track is not TrackVirtual;
|
|
||||||
});
|
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]
|
[Resolved]
|
||||||
private SkinManager skins { get; set; } = null!;
|
private SkinManager skins { get; set; } = null!;
|
||||||
|
|
||||||
private ISkin skin = null!;
|
[Test]
|
||||||
|
public void TestRetrieveOggSample()
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
{
|
||||||
var imported = skins.Import(new ImportTask(TestResources.OpenResource("Archives/ogg-skin.osk"), "ogg-skin.osk")).GetResultSafely();
|
ISkin skin = null!;
|
||||||
skin = imported.PerformRead(skinInfo => skins.GetSkin(skinInfo));
|
|
||||||
|
AddStep("import skin", () => skin = importSkinFromArchives(@"ogg-skin.osk"));
|
||||||
|
AddAssert("sample is non-null", () => skin.GetSample(new SampleInfo(@"sample")) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[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]
|
[Test]
|
||||||
public void TestSampleRetrievalOrder()
|
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
|
private class TestSkin : Skin
|
||||||
{
|
{
|
||||||
public const string SAMPLE_NAME = "test-sample";
|
public const string SAMPLE_NAME = "test-sample";
|
||||||
|
@ -31,8 +31,8 @@ namespace osu.Game.Tests.Visual.Audio
|
|||||||
|
|
||||||
private WaveformTestBeatmap beatmap;
|
private WaveformTestBeatmap beatmap;
|
||||||
|
|
||||||
private OsuSliderBar<int> lowPassSlider;
|
private RoundedSliderBar<int> lowPassSlider;
|
||||||
private OsuSliderBar<int> highPassSlider;
|
private RoundedSliderBar<int> highPassSlider;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Audio
|
|||||||
Text = $"Low Pass: {lowPassFilter.Cutoff}hz",
|
Text = $"Low Pass: {lowPassFilter.Cutoff}hz",
|
||||||
Font = new FontUsage(size: 40)
|
Font = new FontUsage(size: 40)
|
||||||
},
|
},
|
||||||
lowPassSlider = new OsuSliderBar<int>
|
lowPassSlider = new RoundedSliderBar<int>
|
||||||
{
|
{
|
||||||
Width = 500,
|
Width = 500,
|
||||||
Height = 50,
|
Height = 50,
|
||||||
@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Audio
|
|||||||
Text = $"High Pass: {highPassFilter.Cutoff}hz",
|
Text = $"High Pass: {highPassFilter.Cutoff}hz",
|
||||||
Font = new FontUsage(size: 40)
|
Font = new FontUsage(size: 40)
|
||||||
},
|
},
|
||||||
highPassSlider = new OsuSliderBar<int>
|
highPassSlider = new RoundedSliderBar<int>
|
||||||
{
|
{
|
||||||
Width = 500,
|
Width = 500,
|
||||||
Height = 50,
|
Height = 50,
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
{
|
{
|
||||||
public partial class TestSceneTriangleBorderShader : OsuTestScene
|
public partial class TestSceneTriangleBorderShader : OsuTestScene
|
||||||
{
|
{
|
||||||
private readonly TriangleBorder border;
|
private readonly TestTriangle triangle;
|
||||||
|
|
||||||
public TestSceneTriangleBorderShader()
|
public TestSceneTriangleBorderShader()
|
||||||
{
|
{
|
||||||
@ -25,11 +25,11 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.DarkGreen
|
Colour = Color4.DarkGreen
|
||||||
},
|
},
|
||||||
border = new TriangleBorder
|
triangle = new TestTriangle
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = 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();
|
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
|
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]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ShaderManager shaders, IRenderer renderer)
|
private void load(ShaderManager shaders, IRenderer renderer)
|
||||||
{
|
{
|
||||||
@ -62,29 +75,32 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
Texture = renderer.WhitePixel;
|
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)
|
: base(source)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private float thickness;
|
private float thickness;
|
||||||
|
private float texelSize;
|
||||||
|
|
||||||
public override void ApplyState()
|
public override void ApplyState()
|
||||||
{
|
{
|
||||||
base.ApplyState();
|
base.ApplyState();
|
||||||
|
|
||||||
thickness = Source.thickness;
|
thickness = Source.thickness;
|
||||||
|
texelSize = Source.texelSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(IRenderer renderer)
|
public override void Draw(IRenderer renderer)
|
||||||
{
|
{
|
||||||
TextureShader.GetUniform<float>("thickness").UpdateValue(ref thickness);
|
TextureShader.GetUniform<float>("thickness").UpdateValue(ref thickness);
|
||||||
|
TextureShader.GetUniform<float>("texelSize").UpdateValue(ref texelSize);
|
||||||
|
|
||||||
base.Draw(renderer);
|
base.Draw(renderer);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using osu.Game.Graphics.Backgrounds;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Background
|
namespace osu.Game.Tests.Visual.Background
|
||||||
{
|
{
|
||||||
@ -25,7 +26,10 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
ColourLight = Color4.White,
|
ColourLight = Color4.White,
|
||||||
ColourDark = Color4.Gray
|
ColourDark = Color4.Gray,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(0.9f)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -36,6 +40,7 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
|
|
||||||
AddSliderStep("Triangle scale", 0f, 10f, 1f, s => triangles.TriangleScale = s);
|
AddSliderStep("Triangle scale", 0f, 10f, 1f, s => triangles.TriangleScale = s);
|
||||||
AddSliderStep("Seed", 0, 1000, 0, s => triangles.Reset(s));
|
AddSliderStep("Seed", 0, 1000, 0, s => triangles.Reset(s));
|
||||||
|
AddToggleStep("Masking", m => triangles.Masking = m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,14 @@ using osuTK;
|
|||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Background
|
namespace osu.Game.Tests.Visual.Background
|
||||||
{
|
{
|
||||||
public partial class TestSceneTrianglesV2Background : OsuTestScene
|
public partial class TestSceneTrianglesV2Background : OsuTestScene
|
||||||
{
|
{
|
||||||
private readonly TrianglesV2 triangles;
|
private readonly TrianglesV2 triangles;
|
||||||
|
private readonly TrianglesV2 maskedTriangles;
|
||||||
private readonly Box box;
|
private readonly Box box;
|
||||||
|
|
||||||
public TestSceneTrianglesV2Background()
|
public TestSceneTrianglesV2Background()
|
||||||
@ -31,12 +33,20 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0, 5),
|
Spacing = new Vector2(0, 10),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Text = "Masked"
|
||||||
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Size = new Vector2(500, 100),
|
Size = new Vector2(500, 100),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
CornerRadius = 40,
|
CornerRadius = 40,
|
||||||
Children = new Drawable[]
|
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
|
new Container
|
||||||
{
|
{
|
||||||
Size = new Vector2(500, 100),
|
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,
|
Masking = true,
|
||||||
CornerRadius = 40,
|
CornerRadius = 40,
|
||||||
Child = box = new Box
|
Child = box = new Box
|
||||||
@ -75,14 +119,16 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
|
|
||||||
AddSliderStep("Spawn ratio", 0f, 10f, 1f, s =>
|
AddSliderStep("Spawn ratio", 0f, 10f, 1f, s =>
|
||||||
{
|
{
|
||||||
triangles.SpawnRatio = s;
|
triangles.SpawnRatio = maskedTriangles.SpawnRatio = s;
|
||||||
triangles.Reset(1234);
|
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("White colour", () => box.Colour = triangles.Colour = maskedTriangles.Colour = Color4.White);
|
||||||
AddStep("Vertical gradient", () => box.Colour = triangles.Colour = ColourInfo.GradientVertical(Color4.White, Color4.Red));
|
AddStep("Vertical gradient", () => box.Colour = triangles.Colour = maskedTriangles.Colour = ColourInfo.GradientVertical(Color4.White, Color4.Red));
|
||||||
AddStep("Horizontal gradient", () => box.Colour = triangles.Colour = ColourInfo.GradientHorizontal(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("move mouse to common point", () =>
|
AddStep("move mouse to common point", () =>
|
||||||
{
|
{
|
||||||
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
||||||
InputManager.MoveMouseTo(pos);
|
InputManager.MoveMouseTo(pos);
|
||||||
});
|
});
|
||||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||||
|
@ -286,7 +286,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("move mouse to controlpoint", () =>
|
AddStep("move mouse to controlpoint", () =>
|
||||||
{
|
{
|
||||||
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
||||||
InputManager.MoveMouseTo(pos);
|
InputManager.MoveMouseTo(pos);
|
||||||
});
|
});
|
||||||
AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft));
|
AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft));
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Lists;
|
using osu.Framework.Lists;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
@ -28,10 +27,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
public partial class TestSceneBeatmapSkinFallbacks : OsuPlayerTestScene
|
public partial class TestSceneBeatmapSkinFallbacks : OsuPlayerTestScene
|
||||||
{
|
{
|
||||||
private ISkin currentBeatmapSkin;
|
private ISkin currentBeatmapSkin = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private SkinManager skinManager { get; set; }
|
private SkinManager skinManager { get; set; } = null!;
|
||||||
|
|
||||||
protected override bool HasCustomSteps => true;
|
protected override bool HasCustomSteps => true;
|
||||||
|
|
||||||
@ -57,15 +56,15 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
protected bool AssertComponentsFromExpectedSource(GlobalSkinComponentLookup.LookupType target, ISkin expectedSource)
|
protected bool AssertComponentsFromExpectedSource(GlobalSkinComponentLookup.LookupType target, ISkin expectedSource)
|
||||||
{
|
{
|
||||||
var actualComponentsContainer = Player.ChildrenOfType<SkinnableTargetContainer>().First(s => s.Target == target)
|
var targetContainer = Player.ChildrenOfType<SkinnableTargetContainer>().First(s => s.Target == target);
|
||||||
.ChildrenOfType<SkinnableTargetComponentsContainer>().SingleOrDefault();
|
var actualComponentsContainer = targetContainer.ChildrenOfType<Container>().SingleOrDefault(c => c.Parent == targetContainer);
|
||||||
|
|
||||||
if (actualComponentsContainer == null)
|
if (actualComponentsContainer == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var actualInfo = actualComponentsContainer.CreateSkinnableInfo();
|
var actualInfo = actualComponentsContainer.CreateSkinnableInfo();
|
||||||
|
|
||||||
var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource.GetDrawableComponent(new GlobalSkinComponentLookup(target));
|
var expectedComponentsContainer = expectedSource.GetDrawableComponent(new GlobalSkinComponentLookup(target)) as Container;
|
||||||
if (expectedComponentsContainer == null)
|
if (expectedComponentsContainer == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
return almostEqual(actualInfo, expectedInfo);
|
return almostEqual(actualInfo, expectedInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool almostEqual(SkinnableInfo info, SkinnableInfo other) =>
|
private static bool almostEqual(SkinnableInfo info, SkinnableInfo? other) =>
|
||||||
other != null
|
other != null
|
||||||
&& info.Type == other.Type
|
&& info.Type == other.Type
|
||||||
&& info.Anchor == other.Anchor
|
&& info.Anchor == other.Anchor
|
||||||
@ -102,7 +101,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
&& Precision.AlmostEquals(info.Rotation, other.Rotation)
|
&& Precision.AlmostEquals(info.Rotation, other.Rotation)
|
||||||
&& info.Children.SequenceEqual(other.Children, new FuncEqualityComparer<SkinnableInfo>(almostEqual));
|
&& info.Children.SequenceEqual(other.Children, new FuncEqualityComparer<SkinnableInfo>(almostEqual));
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null)
|
||||||
=> new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin);
|
=> new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin);
|
||||||
|
|
||||||
protected override Ruleset CreatePlayerRuleset() => new TestOsuRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new TestOsuRuleset();
|
||||||
@ -111,7 +110,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
private readonly ISkin beatmapSkin;
|
private readonly ISkin beatmapSkin;
|
||||||
|
|
||||||
public CustomSkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio, ISkin beatmapSkin)
|
public CustomSkinWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard, IFrameBasedClock referenceClock, AudioManager audio, ISkin beatmapSkin)
|
||||||
: base(beatmap, storyboard, referenceClock, audio)
|
: base(beatmap, storyboard, referenceClock, audio)
|
||||||
{
|
{
|
||||||
this.beatmapSkin = beatmapSkin;
|
this.beatmapSkin = beatmapSkin;
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using System.Diagnostics;
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -14,16 +16,23 @@ using osu.Game.Rulesets.Objects.Drawables;
|
|||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
public partial class TestSceneGameplaySampleTriggerSource : PlayerTestScene
|
public partial class TestSceneGameplaySampleTriggerSource : PlayerTestScene
|
||||||
{
|
{
|
||||||
private TestGameplaySampleTriggerSource sampleTriggerSource;
|
private TestGameplaySampleTriggerSource sampleTriggerSource = null!;
|
||||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
private Beatmap beatmap;
|
private Beatmap beatmap = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private AudioManager audio { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null)
|
||||||
|
=> new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audio);
|
||||||
|
|
||||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||||
{
|
{
|
||||||
@ -39,12 +48,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
const double start_offset = 8000;
|
const double start_offset = 8000;
|
||||||
const double spacing = 2000;
|
const double spacing = 2000;
|
||||||
|
|
||||||
|
// intentionally start objects a bit late so we can test the case of no alive objects.
|
||||||
double t = start_offset;
|
double t = start_offset;
|
||||||
|
|
||||||
beatmap.HitObjects.AddRange(new[]
|
beatmap.HitObjects.AddRange(new[]
|
||||||
{
|
{
|
||||||
new HitCircle
|
new HitCircle
|
||||||
{
|
{
|
||||||
// intentionally start objects a bit late so we can test the case of no alive objects.
|
|
||||||
StartTime = t += spacing,
|
StartTime = t += spacing,
|
||||||
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
},
|
},
|
||||||
@ -80,42 +90,66 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestCorrectHitObject()
|
public void TestCorrectHitObject()
|
||||||
{
|
{
|
||||||
HitObjectLifetimeEntry nextObjectEntry = null;
|
waitForAliveObjectIndex(null);
|
||||||
|
checkValidObjectIndex(0);
|
||||||
|
|
||||||
AddAssert("no alive objects", () => getNextAliveObject() == null);
|
seekBeforeIndex(0);
|
||||||
|
waitForAliveObjectIndex(0);
|
||||||
|
checkValidObjectIndex(0);
|
||||||
|
|
||||||
AddAssert("check initially correct object", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[0]);
|
AddAssert("first object not hit", () => getNextAliveObject()?.Entry?.Result?.HasResult != true);
|
||||||
|
|
||||||
AddUntilStep("get next object", () =>
|
AddStep("hit first object", () =>
|
||||||
{
|
{
|
||||||
var nextDrawableObject = getNextAliveObject();
|
var next = getNextAliveObject();
|
||||||
|
|
||||||
if (nextDrawableObject != null)
|
if (next != null)
|
||||||
{
|
{
|
||||||
nextObjectEntry = nextDrawableObject.Entry;
|
Debug.Assert(next.Entry?.Result?.HasResult != true);
|
||||||
InputManager.MoveMouseTo(nextDrawableObject.ScreenSpaceDrawQuad.Centre);
|
|
||||||
return true;
|
InputManager.MoveMouseTo(next.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("hit first hitobject", () =>
|
AddAssert("first object hit", () => getNextAliveObject()?.Entry?.Result?.HasResult == true);
|
||||||
{
|
|
||||||
InputManager.Click(MouseButton.Left);
|
|
||||||
return nextObjectEntry.Result?.HasResult == true;
|
|
||||||
});
|
|
||||||
|
|
||||||
AddAssert("check correct object after hit", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[1]);
|
checkValidObjectIndex(1);
|
||||||
|
|
||||||
AddUntilStep("check correct object after miss", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[2]);
|
// Still object 1 as it's not hit yet.
|
||||||
AddUntilStep("check correct object after miss", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[3]);
|
seekBeforeIndex(1);
|
||||||
|
waitForAliveObjectIndex(1);
|
||||||
|
checkValidObjectIndex(1);
|
||||||
|
|
||||||
AddUntilStep("no alive objects", () => getNextAliveObject() == null);
|
seekBeforeIndex(2);
|
||||||
AddAssert("check correct object after none alive", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[3]);
|
waitForAliveObjectIndex(2);
|
||||||
|
checkValidObjectIndex(2);
|
||||||
|
|
||||||
|
seekBeforeIndex(3);
|
||||||
|
waitForAliveObjectIndex(3);
|
||||||
|
checkValidObjectIndex(3);
|
||||||
|
|
||||||
|
AddStep("Seek into future", () => Beatmap.Value.Track.Seek(beatmap.HitObjects.Last().GetEndTime() + 10000));
|
||||||
|
|
||||||
|
waitForAliveObjectIndex(null);
|
||||||
|
checkValidObjectIndex(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DrawableHitObject getNextAliveObject() =>
|
private void seekBeforeIndex(int index) =>
|
||||||
|
AddStep($"seek to just before object {index}", () => Beatmap.Value.Track.Seek(beatmap.HitObjects[index].StartTime - 100));
|
||||||
|
|
||||||
|
private void waitForAliveObjectIndex(int? index)
|
||||||
|
{
|
||||||
|
if (index == null)
|
||||||
|
AddUntilStep("wait for no alive objects", getNextAliveObject, () => Is.Null);
|
||||||
|
else
|
||||||
|
AddUntilStep($"wait for next alive to be {index}", () => getNextAliveObject()?.HitObject, () => Is.EqualTo(beatmap.HitObjects[index.Value]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkValidObjectIndex(int index) =>
|
||||||
|
AddAssert($"check valid object is {index}", () => sampleTriggerSource.GetMostValidObject(), () => Is.EqualTo(beatmap.HitObjects[index]));
|
||||||
|
|
||||||
|
private DrawableHitObject? getNextAliveObject() =>
|
||||||
Player.DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.FirstOrDefault();
|
Player.DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.FirstOrDefault();
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
24
osu.Game.Tests/Visual/Gameplay/TestSceneLetterboxOverlay.cs
Normal file
24
osu.Game.Tests/Visual/Gameplay/TestSceneLetterboxOverlay.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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Screens.Play.Break;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public partial class TestSceneLetterboxOverlay : OsuTestScene
|
||||||
|
{
|
||||||
|
public TestSceneLetterboxOverlay()
|
||||||
|
{
|
||||||
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
new LetterboxOverlay()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
@ -89,7 +90,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Player.OnUpdate += _ =>
|
Player.OnUpdate += _ =>
|
||||||
{
|
{
|
||||||
double currentTime = Player.GameplayClockContainer.CurrentTime;
|
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;
|
lastTime = currentTime;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -1,36 +1,98 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using System.Linq;
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
public partial class TestSceneReplayPlayer : RateAdjustedBeatmapTestScene
|
public partial class TestSceneReplayPlayer : RateAdjustedBeatmapTestScene
|
||||||
{
|
{
|
||||||
protected TestReplayPlayer Player;
|
protected TestReplayPlayer Player = null!;
|
||||||
|
|
||||||
public override void SetUpSteps()
|
|
||||||
{
|
|
||||||
base.SetUpSteps();
|
|
||||||
|
|
||||||
AddStep("Initialise player", () => Player = CreatePlayer(new OsuRuleset()));
|
|
||||||
AddStep("Load player", () => LoadScreen(Player));
|
|
||||||
AddUntilStep("player loaded", () => Player.IsLoaded);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestPause()
|
public void TestPauseViaSpace()
|
||||||
{
|
{
|
||||||
|
loadPlayerWithBeatmap();
|
||||||
|
|
||||||
double? lastTime = null;
|
double? lastTime = null;
|
||||||
|
|
||||||
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
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 TestPauseViaSpaceWithSkip()
|
||||||
|
{
|
||||||
|
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||||
|
{
|
||||||
|
BeatmapInfo = { AudioLeadIn = 60000 }
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for skip overlay", () => Player.ChildrenOfType<SkipOverlay>().First().IsButtonVisible);
|
||||||
|
|
||||||
|
AddStep("Skip with space", () => InputManager.Key(Key.Space));
|
||||||
|
|
||||||
|
AddAssert("Player not paused", () => !Player.DrawableRuleset.IsPaused.Value);
|
||||||
|
|
||||||
|
double? lastTime = null;
|
||||||
|
|
||||||
|
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
loadPlayerWithBeatmap();
|
||||||
|
|
||||||
|
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", () =>
|
AddUntilStep("Time stopped progressing", () =>
|
||||||
{
|
{
|
||||||
@ -49,6 +111,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSeekBackwards()
|
public void TestSeekBackwards()
|
||||||
{
|
{
|
||||||
|
loadPlayerWithBeatmap();
|
||||||
|
|
||||||
double? lastTime = null;
|
double? lastTime = null;
|
||||||
|
|
||||||
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
@ -65,6 +129,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSeekForwards()
|
public void TestSeekForwards()
|
||||||
{
|
{
|
||||||
|
loadPlayerWithBeatmap();
|
||||||
|
|
||||||
double? lastTime = null;
|
double? lastTime = null;
|
||||||
|
|
||||||
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
@ -78,12 +144,26 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddAssert("Jumped forwards", () => Player.GameplayClockContainer.CurrentTime - lastTime > 500);
|
AddAssert("Jumped forwards", () => Player.GameplayClockContainer.CurrentTime - lastTime > 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TestReplayPlayer CreatePlayer(Ruleset ruleset)
|
private void loadPlayerWithBeatmap(IBeatmap? beatmap = null)
|
||||||
{
|
{
|
||||||
Beatmap.Value = CreateWorkingBeatmap(ruleset.RulesetInfo);
|
AddStep("create player", () =>
|
||||||
|
{
|
||||||
|
CreatePlayer(new OsuRuleset(), beatmap);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("Load player", () => LoadScreen(Player));
|
||||||
|
AddUntilStep("player loaded", () => Player.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void CreatePlayer(Ruleset ruleset, IBeatmap? beatmap = null)
|
||||||
|
{
|
||||||
|
Beatmap.Value = beatmap != null
|
||||||
|
? CreateWorkingBeatmap(beatmap)
|
||||||
|
: CreateWorkingBeatmap(ruleset.RulesetInfo);
|
||||||
|
|
||||||
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
|
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
|
||||||
|
|
||||||
return new TestReplayPlayer(false);
|
Player = new TestReplayPlayer(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,11 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Overlays.SkinEditor;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Skinning.Editor;
|
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
@ -7,9 +7,9 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.SkinEditor;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Skinning.Editor;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
|
@ -8,11 +8,11 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Overlays.SkinEditor;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Skinning.Editor;
|
|
||||||
using osu.Game.Tests.Gameplay;
|
using osu.Game.Tests.Gameplay;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ using osu.Game.Online.Multiplayer;
|
|||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Screens.OnlinePlay;
|
||||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||||
@ -85,6 +86,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||||
|
|
||||||
AddUntilStep("wait for join", () => MultiplayerClient.RoomJoined);
|
AddUntilStep("wait for join", () => MultiplayerClient.RoomJoined);
|
||||||
|
AddUntilStep("wait for ongoing operation to complete", () => !(CurrentScreen as OnlinePlayScreen).ChildrenOfType<OngoingOperationTracker>().Single().InProgress.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -15,7 +15,6 @@ using osu.Game.Rulesets.Catch;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Screens.OnlinePlay;
|
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -44,14 +43,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[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()
|
public void TestItemAddedToTheEndOfQueue()
|
||||||
{
|
{
|
||||||
addItem(() => OtherBeatmap);
|
addItem(() => OtherBeatmap);
|
||||||
@ -64,7 +55,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[FlakyTest] // See above
|
|
||||||
public void TestNextItemSelectedAfterGameplayFinish()
|
public void TestNextItemSelectedAfterGameplayFinish()
|
||||||
{
|
{
|
||||||
addItem(() => OtherBeatmap);
|
addItem(() => OtherBeatmap);
|
||||||
@ -82,7 +72,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[FlakyTest] // See above
|
|
||||||
public void TestItemsNotClearedWhenSwitchToHostOnlyMode()
|
public void TestItemsNotClearedWhenSwitchToHostOnlyMode()
|
||||||
{
|
{
|
||||||
addItem(() => OtherBeatmap);
|
addItem(() => OtherBeatmap);
|
||||||
@ -98,7 +87,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[FlakyTest] // See above
|
|
||||||
public void TestCorrectItemSelectedAfterNewItemAdded()
|
public void TestCorrectItemSelectedAfterNewItemAdded()
|
||||||
{
|
{
|
||||||
addItem(() => OtherBeatmap);
|
addItem(() => OtherBeatmap);
|
||||||
@ -106,7 +94,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[FlakyTest] // See above
|
|
||||||
public void TestCorrectRulesetSelectedAfterNewItemAdded()
|
public void TestCorrectRulesetSelectedAfterNewItemAdded()
|
||||||
{
|
{
|
||||||
addItem(() => OtherBeatmap, new CatchRuleset().RulesetInfo);
|
addItem(() => OtherBeatmap, new CatchRuleset().RulesetInfo);
|
||||||
@ -124,7 +111,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[FlakyTest] // See above
|
|
||||||
public void TestCorrectModsSelectedAfterNewItemAdded()
|
public void TestCorrectModsSelectedAfterNewItemAdded()
|
||||||
{
|
{
|
||||||
addItem(() => OtherBeatmap, mods: new Mod[] { new OsuModDoubleTime() });
|
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 song select", () => (songSelect = CurrentSubScreen as Screens.Select.SongSelect) != null);
|
||||||
AddUntilStep("wait for loaded", () => songSelect.AsNonNull().BeatmapSetsLoaded);
|
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)
|
if (ruleset != null)
|
||||||
AddStep($"set {ruleset.Name} ruleset", () => songSelect.AsNonNull().Ruleset.Value = ruleset);
|
AddStep($"set {ruleset.Name} ruleset", () => songSelect.AsNonNull().Ruleset.Value = ruleset);
|
||||||
|
@ -50,14 +50,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[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()
|
public void TestItemStillSelectedAfterChangeToSameBeatmap()
|
||||||
{
|
{
|
||||||
selectNewItem(() => InitialBeatmap);
|
selectNewItem(() => InitialBeatmap);
|
||||||
@ -66,7 +58,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[FlakyTest] // See above
|
|
||||||
public void TestItemStillSelectedAfterChangeToOtherBeatmap()
|
public void TestItemStillSelectedAfterChangeToOtherBeatmap()
|
||||||
{
|
{
|
||||||
selectNewItem(() => OtherBeatmap);
|
selectNewItem(() => OtherBeatmap);
|
||||||
@ -75,7 +66,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[FlakyTest] // See above
|
|
||||||
public void TestOnlyLastItemChangedAfterGameplayFinished()
|
public void TestOnlyLastItemChangedAfterGameplayFinished()
|
||||||
{
|
{
|
||||||
RunGameplay();
|
RunGameplay();
|
||||||
@ -90,7 +80,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[FlakyTest] // See above
|
|
||||||
public void TestAddItemsAsHost()
|
public void TestAddItemsAsHost()
|
||||||
{
|
{
|
||||||
addItem(() => OtherBeatmap);
|
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);
|
AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
|
||||||
|
|
||||||
BeatmapInfo otherBeatmap = null;
|
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()));
|
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(otherBeatmap = beatmap()));
|
||||||
|
|
||||||
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
|
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 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()));
|
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(beatmap()));
|
||||||
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
|
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,8 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Overlays.Settings.Sections;
|
using osu.Game.Overlays.Settings.Sections;
|
||||||
|
using osu.Game.Overlays.SkinEditor;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Skinning.Editor;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Navigation
|
namespace osu.Game.Tests.Visual.Navigation
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.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;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Navigation
|
namespace osu.Game.Tests.Visual.Navigation
|
||||||
{
|
{
|
||||||
@ -23,11 +26,13 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
{
|
{
|
||||||
private HeadlessGameHost ipcSenderHost = null!;
|
private HeadlessGameHost ipcSenderHost = null!;
|
||||||
|
|
||||||
private OsuSchemeLinkIPCChannel osuSchemeLinkIPCReceiver = null!;
|
|
||||||
private OsuSchemeLinkIPCChannel osuSchemeLinkIPCSender = null!;
|
private OsuSchemeLinkIPCChannel osuSchemeLinkIPCSender = null!;
|
||||||
|
private ArchiveImportIPCChannel archiveImportIPCSender = null!;
|
||||||
|
|
||||||
private const int requested_beatmap_set_id = 1;
|
private const int requested_beatmap_set_id = 1;
|
||||||
|
|
||||||
|
protected override TestOsuGame CreateTestGame() => new IpcGame(LocalStorage, API);
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private GameHost gameHost { get; set; } = null!;
|
private GameHost gameHost { get; set; } = null!;
|
||||||
|
|
||||||
@ -56,11 +61,11 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
AddStep("create IPC receiver channel", () => osuSchemeLinkIPCReceiver = new OsuSchemeLinkIPCChannel(gameHost, Game));
|
AddStep("create IPC sender channels", () =>
|
||||||
AddStep("create IPC sender channel", () =>
|
|
||||||
{
|
{
|
||||||
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { BindIPC = true });
|
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { BindIPC = true });
|
||||||
osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost);
|
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);
|
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()
|
public override void TearDownSteps()
|
||||||
{
|
{
|
||||||
AddStep("dispose IPC receiver", () => osuSchemeLinkIPCReceiver.Dispose());
|
AddStep("dispose IPC senders", () =>
|
||||||
AddStep("dispose IPC sender", () =>
|
|
||||||
{
|
{
|
||||||
osuSchemeLinkIPCSender.Dispose();
|
osuSchemeLinkIPCSender.Dispose();
|
||||||
|
archiveImportIPCSender.Dispose();
|
||||||
ipcSenderHost.Dispose();
|
ipcSenderHost.Dispose();
|
||||||
});
|
});
|
||||||
base.TearDownSteps();
|
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));
|
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]
|
[Test]
|
||||||
public void TestMainOverlaysClosesNotificationOverlay()
|
public void TestMainOverlaysClosesNotificationOverlay()
|
||||||
{
|
{
|
||||||
|
@ -12,11 +12,11 @@ using osu.Framework.Screens;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Overlays.SkinEditor;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||||
using osu.Game.Skinning.Editor;
|
|
||||||
using osu.Game.Tests.Beatmaps.IO;
|
using osu.Game.Tests.Beatmaps.IO;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
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 } })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestEditActivity()
|
public void TestEditActivity()
|
||||||
{
|
{
|
||||||
AddStep("Set activity", () => api.Activity.Value = new UserActivity.Editing(new BeatmapInfo()));
|
AddStep("Set activity", () => api.Activity.Value = new UserActivity.EditingBeatmap(new BeatmapInfo()));
|
||||||
|
|
||||||
AddStep("Run command", () => Add(new NowPlayingCommand(new Channel())));
|
AddStep("Run command", () => Add(new NowPlayingCommand(new Channel())));
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -107,14 +109,16 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddStep("set online status", () => status.Value = new UserStatusOnline());
|
AddStep("set online status", () => status.Value = new UserStatusOnline());
|
||||||
|
|
||||||
AddStep("idle", () => activity.Value = null);
|
AddStep("idle", () => activity.Value = null);
|
||||||
AddStep("spectating", () => activity.Value = new UserActivity.Spectating());
|
AddStep("watching replay", () => activity.Value = new UserActivity.WatchingReplay(createScore(@"nats")));
|
||||||
|
AddStep("spectating user", () => activity.Value = new UserActivity.SpectatingUser(createScore(@"mrekk")));
|
||||||
AddStep("solo (osu!)", () => activity.Value = soloGameStatusForRuleset(0));
|
AddStep("solo (osu!)", () => activity.Value = soloGameStatusForRuleset(0));
|
||||||
AddStep("solo (osu!taiko)", () => activity.Value = soloGameStatusForRuleset(1));
|
AddStep("solo (osu!taiko)", () => activity.Value = soloGameStatusForRuleset(1));
|
||||||
AddStep("solo (osu!catch)", () => activity.Value = soloGameStatusForRuleset(2));
|
AddStep("solo (osu!catch)", () => activity.Value = soloGameStatusForRuleset(2));
|
||||||
AddStep("solo (osu!mania)", () => activity.Value = soloGameStatusForRuleset(3));
|
AddStep("solo (osu!mania)", () => activity.Value = soloGameStatusForRuleset(3));
|
||||||
AddStep("choosing", () => activity.Value = new UserActivity.ChoosingBeatmap());
|
AddStep("choosing", () => activity.Value = new UserActivity.ChoosingBeatmap());
|
||||||
AddStep("editing", () => activity.Value = new UserActivity.Editing(null));
|
AddStep("editing beatmap", () => activity.Value = new UserActivity.EditingBeatmap(null));
|
||||||
AddStep("modding", () => activity.Value = new UserActivity.Modding());
|
AddStep("modding beatmap", () => activity.Value = new UserActivity.ModdingBeatmap(null));
|
||||||
|
AddStep("testing beatmap", () => activity.Value = new UserActivity.TestingBeatmap(null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -132,6 +136,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(null, rulesetStore.GetRuleset(rulesetId));
|
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(null, rulesetStore.GetRuleset(rulesetId));
|
||||||
|
|
||||||
|
private ScoreInfo createScore(string name) => new ScoreInfo(new TestBeatmap(Ruleset.Value).BeatmapInfo)
|
||||||
|
{
|
||||||
|
User = new APIUser
|
||||||
|
{
|
||||||
|
Username = name,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private partial class TestUserListPanel : UserListPanel
|
private partial class TestUserListPanel : UserListPanel
|
||||||
{
|
{
|
||||||
public TestUserListPanel(APIUser user)
|
public TestUserListPanel(APIUser user)
|
||||||
|
@ -90,7 +90,9 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
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[] { "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[]
|
ProfileOrder = new[]
|
||||||
{
|
{
|
||||||
@ -119,6 +121,12 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
|
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
TournamentBanner = new TournamentBanner
|
||||||
|
{
|
||||||
|
Id = 13926,
|
||||||
|
TournamentId = 35,
|
||||||
|
ImageLowRes = "https://assets.ppy.sh/tournament-banners/official/owc2022/profile/winner_US.jpg",
|
||||||
|
},
|
||||||
Badges = new[]
|
Badges = new[]
|
||||||
{
|
{
|
||||||
new Badge
|
new Badge
|
||||||
|
@ -24,17 +24,26 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
{
|
{
|
||||||
public partial class TestSceneAccuracyCircle : OsuTestScene
|
public partial class TestSceneAccuracyCircle : OsuTestScene
|
||||||
{
|
{
|
||||||
[TestCase(0.2, ScoreRank.D)]
|
[TestCase(0)]
|
||||||
[TestCase(0.5, ScoreRank.D)]
|
[TestCase(0.2)]
|
||||||
[TestCase(0.75, ScoreRank.C)]
|
[TestCase(0.5)]
|
||||||
[TestCase(0.85, ScoreRank.B)]
|
[TestCase(0.6999)]
|
||||||
[TestCase(0.925, ScoreRank.A)]
|
[TestCase(0.7)]
|
||||||
[TestCase(0.975, ScoreRank.S)]
|
[TestCase(0.75)]
|
||||||
[TestCase(0.9999, ScoreRank.S)]
|
[TestCase(0.7999)]
|
||||||
[TestCase(1, ScoreRank.X)]
|
[TestCase(0.8)]
|
||||||
public void TestRank(double accuracy, ScoreRank rank)
|
[TestCase(0.85)]
|
||||||
|
[TestCase(0.8999)]
|
||||||
|
[TestCase(0.9)]
|
||||||
|
[TestCase(0.925)]
|
||||||
|
[TestCase(0.9499)]
|
||||||
|
[TestCase(0.95)]
|
||||||
|
[TestCase(0.975)]
|
||||||
|
[TestCase(0.9999)]
|
||||||
|
[TestCase(1)]
|
||||||
|
public void TestRank(double accuracy)
|
||||||
{
|
{
|
||||||
var score = createScore(accuracy, rank);
|
var score = createScore(accuracy, ScoreProcessor.RankFromAccuracy(accuracy));
|
||||||
|
|
||||||
addCircleStep(score);
|
addCircleStep(score);
|
||||||
}
|
}
|
||||||
|
146
osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs
Normal file
146
osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
|
using osu.Game.Screens.Select.FooterV2;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
|
{
|
||||||
|
public partial class TestSceneSongSelectFooterV2 : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private FooterButtonRandomV2 randomButton = null!;
|
||||||
|
private FooterButtonModsV2 modsButton = null!;
|
||||||
|
|
||||||
|
private bool nextRandomCalled;
|
||||||
|
private bool previousRandomCalled;
|
||||||
|
|
||||||
|
private DummyOverlay overlay = null!;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
nextRandomCalled = false;
|
||||||
|
previousRandomCalled = false;
|
||||||
|
|
||||||
|
FooterV2 footer;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
footer = new FooterV2
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre
|
||||||
|
},
|
||||||
|
overlay = new DummyOverlay()
|
||||||
|
};
|
||||||
|
|
||||||
|
footer.AddButton(modsButton = new FooterButtonModsV2(), overlay);
|
||||||
|
footer.AddButton(randomButton = new FooterButtonRandomV2
|
||||||
|
{
|
||||||
|
NextRandom = () => nextRandomCalled = true,
|
||||||
|
PreviousRandom = () => previousRandomCalled = true
|
||||||
|
});
|
||||||
|
footer.AddButton(new FooterButtonOptionsV2());
|
||||||
|
|
||||||
|
overlay.Hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestState()
|
||||||
|
{
|
||||||
|
AddToggleStep("set options enabled state", state => this.ChildrenOfType<FooterButtonV2>().Last().Enabled.Value = state);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRandom()
|
||||||
|
{
|
||||||
|
AddStep("press F2", () => InputManager.Key(Key.F2));
|
||||||
|
AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRandomViaMouse()
|
||||||
|
{
|
||||||
|
AddStep("click button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(randomButton);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRewind()
|
||||||
|
{
|
||||||
|
AddStep("press Shift+F2", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LShift);
|
||||||
|
InputManager.PressKey(Key.F2);
|
||||||
|
InputManager.ReleaseKey(Key.F2);
|
||||||
|
InputManager.ReleaseKey(Key.LShift);
|
||||||
|
});
|
||||||
|
AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRewindViaShiftMouseLeft()
|
||||||
|
{
|
||||||
|
AddStep("shift + click button", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LShift);
|
||||||
|
InputManager.MoveMouseTo(randomButton);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.LShift);
|
||||||
|
});
|
||||||
|
AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRewindViaMouseRight()
|
||||||
|
{
|
||||||
|
AddStep("right click button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(randomButton);
|
||||||
|
InputManager.Click(MouseButton.Right);
|
||||||
|
});
|
||||||
|
AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOverlayPresent()
|
||||||
|
{
|
||||||
|
AddStep("Press F1", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(modsButton);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("Overlay visible", () => overlay.State.Value == Visibility.Visible);
|
||||||
|
AddStep("Hide", () => overlay.Hide());
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class DummyOverlay : ShearedOverlayContainer
|
||||||
|
{
|
||||||
|
public DummyOverlay()
|
||||||
|
: base(OverlayColourScheme.Green)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Header.Title = "An overlay";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
AddStep($"Set {name} slider to {value}", () =>
|
AddStep($"Set {name} slider to {value}", () =>
|
||||||
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
|
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
|
||||||
.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value = value);
|
.ChildrenOfType<RoundedSliderBar<float>>().First().Current.Value = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkBindableAtValue(string name, float? expectedValue)
|
private void checkBindableAtValue(string name, float? expectedValue)
|
||||||
@ -275,7 +275,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
AddAssert($"Slider {name} at {expectedValue}", () =>
|
AddAssert($"Slider {name} at {expectedValue}", () =>
|
||||||
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
|
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
|
||||||
.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value == expectedValue);
|
.ChildrenOfType<RoundedSliderBar<float>>().First().Current.Value == expectedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBeatmapWithDifficultyParameters(float value)
|
private void setBeatmapWithDifficultyParameters(float value)
|
||||||
|
@ -270,7 +270,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
createScreen();
|
createScreen();
|
||||||
AddStep("select difficulty adjust via panel", () => getPanelForMod(typeof(OsuModDifficultyAdjust)).TriggerClick());
|
AddStep("select difficulty adjust via panel", () => getPanelForMod(typeof(OsuModDifficultyAdjust)).TriggerClick());
|
||||||
|
|
||||||
AddStep("set setting", () => modSelectOverlay.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value = 8);
|
AddStep("set setting", () => modSelectOverlay.ChildrenOfType<RoundedSliderBar<float>>().First().Current.Value = 8);
|
||||||
|
|
||||||
AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == 8);
|
AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == 8);
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public partial class TestSceneShearedSliderBar : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||||
|
|
||||||
|
private readonly BindableDouble current = new BindableDouble(5)
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Child = new ShearedSliderBar<double>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Current = current,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Width = 0.4f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user