1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 02:42:54 +08:00

Merge branch 'master' into spun-out

This commit is contained in:
杜Nate 2020-02-08 13:54:39 +08:00 committed by GitHub
commit 8d480c794a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
493 changed files with 8980 additions and 4433 deletions

View File

@ -135,7 +135,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,new,abstract
csharp_style_expression_bodied_accessors = true:warning csharp_style_expression_bodied_accessors = true:warning
csharp_style_expression_bodied_constructors = false:none csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_indexers = true:warning csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_methods = true:silent csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = true:warning csharp_style_expression_bodied_operators = true:warning
csharp_style_expression_bodied_properties = true:warning csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_local_functions = true:silent csharp_style_expression_bodied_local_functions = true:silent

3
.gitignore vendored
View File

@ -331,3 +331,6 @@ fastlane/report.xml
# inspectcode # inspectcode
inspectcodereport.xml inspectcodereport.xml
inspectcode inspectcode
# BenchmarkDotNet
/BenchmarkDotNet.Artifacts

View File

@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Benchmarks" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/netcoreapp3.1/osu.Game.Benchmarks.dll" />
<option name="PROGRAM_PARAMETERS" value="--filter *" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
</method>
</configuration>
</component>

View File

@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! SDL" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="--sdl" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Desktop/osu.Desktop.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>
</configuration>
</component>

19
.vscode/launch.json vendored
View File

@ -1,6 +1,7 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [{ "configurations": [
{
"name": "osu! (Debug)", "name": "osu! (Debug)",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
@ -50,7 +51,8 @@
} }
}, },
"console": "internalConsole" "console": "internalConsole"
}, { },
{
"name": "osu! (Tests, Release)", "name": "osu! (Tests, Release)",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
@ -139,6 +141,19 @@
}, },
"console": "internalConsole" "console": "internalConsole"
}, },
{
"name": "Benchmark",
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/osu.Game.Benchmarks/bin/Release/netcoreapp3.1/osu.Game.Benchmarks.dll",
"args": [
"--filter",
"*"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build benchmarks",
"console": "internalConsole"
},
{ {
"name": "Cake: Debug Script", "name": "Cake: Debug Script",
"type": "coreclr", "type": "coreclr",

22
.vscode/tasks.json vendored
View File

@ -2,7 +2,8 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558 // See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format // for the documentation about the tasks.json format
"version": "2.0.0", "version": "2.0.0",
"tasks": [{ "tasks": [
{
"label": "Build osu! (Debug)", "label": "Build osu! (Debug)",
"type": "shell", "type": "shell",
"command": "dotnet", "command": "dotnet",
@ -78,7 +79,8 @@
], ],
"group": "build", "group": "build",
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, { },
{
"label": "Build tournament tests (Release)", "label": "Build tournament tests (Release)",
"type": "shell", "type": "shell",
"command": "dotnet", "command": "dotnet",
@ -94,6 +96,22 @@
"group": "build", "group": "build",
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{
"label": "Build benchmarks",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Benchmarks",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{ {
"label": "Restore (netcoreapp3.1)", "label": "Restore (netcoreapp3.1)",
"type": "shell", "type": "shell",

View File

@ -40,7 +40,7 @@
<RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl> <RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl>
<PackageReleaseNotes>Automated release.</PackageReleaseNotes> <PackageReleaseNotes>Automated release.</PackageReleaseNotes>
<Company>ppy Pty Ltd</Company> <Company>ppy Pty Ltd</Company>
<Copyright>Copyright (c) 2019 ppy Pty Ltd</Copyright> <Copyright>Copyright (c) 2020 ppy Pty Ltd</Copyright>
<PackageTags>osu game</PackageTags> <PackageTags>osu game</PackageTags>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -1,7 +1,7 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
CFPropertyList (3.0.1) CFPropertyList (3.0.2)
addressable (2.7.0) addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 5.0)
atomos (0.1.3) atomos (0.1.3)
@ -18,8 +18,8 @@ GEM
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.5) dotenv (2.7.5)
emoji_regex (1.0.1) emoji_regex (1.0.1)
excon (0.67.0) excon (0.71.1)
faraday (0.15.4) faraday (0.17.3)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6) faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4) faraday (>= 0.7.4)
@ -27,7 +27,7 @@ GEM
faraday_middleware (0.13.1) faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0) faraday (>= 0.7.4, < 1.0)
fastimage (2.1.7) fastimage (2.1.7)
fastlane (2.133.0) fastlane (2.140.0)
CFPropertyList (>= 2.3, < 4.0.0) CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0) addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0) babosa (>= 1.0.2, < 2.0.0)
@ -36,13 +36,13 @@ GEM
commander-fastlane (>= 4.4.6, < 5.0.0) commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0) dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 2.0) emoji_regex (>= 0.1, < 2.0)
excon (>= 0.45.0, < 1.0.0) excon (>= 0.71.0, < 1.0.0)
faraday (< 0.16.0) faraday (~> 0.17)
faraday-cookie_jar (~> 0.0.6) faraday-cookie_jar (~> 0.0.6)
faraday_middleware (< 0.16.0) faraday_middleware (~> 0.13.1)
fastimage (>= 2.1.0, < 3.0.0) fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0) gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.21.2, < 0.24.0) google-api-client (>= 0.29.2, < 0.37.0)
google-cloud-storage (>= 1.15.0, < 2.0.0) google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0) highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0) json (< 3.0.0)
@ -61,56 +61,58 @@ GEM
tty-screen (>= 0.6.3, < 1.0.0) tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0) word_wrap (~> 1.0.0)
xcodeproj (>= 1.8.1, < 2.0.0) xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0) xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3) xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-clean_testflight_testers (0.3.0) fastlane-plugin-clean_testflight_testers (0.3.0)
fastlane-plugin-souyuz (0.8.1) fastlane-plugin-souyuz (0.9.1)
souyuz (>= 0.8.1) souyuz (= 0.9.1)
fastlane-plugin-xamarin (0.6.3) fastlane-plugin-xamarin (0.6.3)
gh_inspector (1.1.3) gh_inspector (1.1.3)
google-api-client (0.23.9) google-api-client (0.36.4)
addressable (~> 2.5, >= 2.5.1) addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.5, < 0.7.0) googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0) httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0) mini_mime (~> 1.0)
representable (~> 3.0) representable (~> 3.0)
retriable (>= 2.0, < 4.0) retriable (>= 2.0, < 4.0)
signet (~> 0.9) signet (~> 0.12)
google-cloud-core (1.3.1) google-cloud-core (1.5.0)
google-cloud-env (~> 1.0) google-cloud-env (~> 1.0)
google-cloud-env (1.2.1) google-cloud-errors (~> 1.0)
google-cloud-env (1.3.0)
faraday (~> 0.11) faraday (~> 0.11)
google-cloud-storage (1.16.0) google-cloud-errors (1.0.0)
google-cloud-storage (1.25.1)
addressable (~> 2.5)
digest-crc (~> 0.4) digest-crc (~> 0.4)
google-api-client (~> 0.23) google-api-client (~> 0.33)
google-cloud-core (~> 1.2) google-cloud-core (~> 1.2)
googleauth (>= 0.6.2, < 0.10.0) googleauth (~> 0.9)
googleauth (0.6.7) mini_mime (~> 1.0)
googleauth (0.10.0)
faraday (~> 0.12) faraday (~> 0.12)
jwt (>= 1.4, < 3.0) jwt (>= 1.4, < 3.0)
memoist (~> 0.16) memoist (~> 0.16)
multi_json (~> 1.11) multi_json (~> 1.11)
os (>= 0.9, < 2.0) os (>= 0.9, < 2.0)
signet (~> 0.7) signet (~> 0.12)
highline (1.7.10) highline (1.7.10)
http-cookie (1.0.3) http-cookie (1.0.3)
domain_name (~> 0.5) domain_name (~> 0.5)
httpclient (2.8.3) httpclient (2.8.3)
json (2.2.0) json (2.3.0)
jwt (2.1.0) jwt (2.1.0)
memoist (0.16.0) memoist (0.16.2)
mime-types (3.3) mini_magick (4.10.1)
mime-types-data (~> 3.2015) mini_mime (1.0.2)
mime-types-data (3.2019.1009)
mini_magick (4.9.5)
mini_portile2 (2.4.0) mini_portile2 (2.4.0)
multi_json (1.13.1) multi_json (1.14.1)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
nanaimo (0.2.6) nanaimo (0.2.6)
naturally (2.2.0) naturally (2.2.0)
nokogiri (1.10.4) nokogiri (1.10.7)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
os (1.0.1) os (1.0.1)
plist (3.5.0) plist (3.5.0)
@ -128,12 +130,12 @@ GEM
faraday (~> 0.9) faraday (~> 0.9)
jwt (>= 1.5, < 3.0) jwt (>= 1.5, < 3.0)
multi_json (~> 1.10) multi_json (~> 1.10)
simctl (1.6.6) simctl (1.6.7)
CFPropertyList CFPropertyList
naturally naturally
slack-notifier (2.3.2) slack-notifier (2.3.2)
souyuz (0.8.1) souyuz (0.9.1)
fastlane (>= 2.29.0) fastlane (>= 1.103.0)
highline (~> 1.7) highline (~> 1.7)
nokogiri (~> 1.7) nokogiri (~> 1.7)
terminal-notifier (2.0.0) terminal-notifier (2.0.0)
@ -141,15 +143,15 @@ GEM
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (~> 1.1, >= 1.1.1)
tty-cursor (0.7.0) tty-cursor (0.7.0)
tty-screen (0.7.0) tty-screen (0.7.0)
tty-spinner (0.9.1) tty-spinner (0.9.2)
tty-cursor (~> 0.7) tty-cursor (~> 0.7)
uber (0.1.0) uber (0.1.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.6) unf_ext (0.0.7.6)
unicode-display_width (1.6.0) unicode-display_width (1.6.1)
word_wrap (1.0.0) word_wrap (1.0.0)
xcodeproj (1.12.0) xcodeproj (1.14.0)
CFPropertyList (>= 2.3.3, < 4.0) CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3) atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0) claide (>= 1.0.2, < 2.0)

View File

@ -1,4 +1,4 @@
Copyright (c) 2019 ppy Pty Ltd <contact@ppy.sh>. Copyright (c) 2020 ppy Pty Ltd <contact@ppy.sh>.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -111,7 +111,6 @@ platform :ios do
souyuz( souyuz(
platform: "ios", platform: "ios",
build_target: "osu_iOS",
plist_path: "../osu.iOS/Info.plist" plist_path: "../osu.iOS/Info.plist"
) )
end end

View File

@ -54,6 +54,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.111.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2020.207.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -3,6 +3,7 @@
"path": "osu.sln", "path": "osu.sln",
"projects": [ "projects": [
"osu.Desktop\\osu.Desktop.csproj", "osu.Desktop\\osu.Desktop.csproj",
"osu.Game.Benchmarks\\osu.Game.Benchmarks.csproj",
"osu.Game.Rulesets.Catch.Tests\\osu.Game.Rulesets.Catch.Tests.csproj", "osu.Game.Rulesets.Catch.Tests\\osu.Game.Rulesets.Catch.Tests.csproj",
"osu.Game.Rulesets.Catch\\osu.Game.Rulesets.Catch.csproj", "osu.Game.Rulesets.Catch\\osu.Game.Rulesets.Catch.csproj",
"osu.Game.Rulesets.Mania.Tests\\osu.Game.Rulesets.Mania.Tests.csproj", "osu.Game.Rulesets.Mania.Tests\\osu.Game.Rulesets.Mania.Tests.csproj",

View File

@ -75,6 +75,9 @@ namespace osu.Desktop
private void updateStatus() private void updateStatus()
{ {
if (!client.IsInitialized)
return;
if (status.Value is UserStatusOffline) if (status.Value is UserStatusOffline)
{ {
client.ClearPresence(); client.ClearPresence();

View File

@ -22,8 +22,9 @@ namespace osu.Desktop
{ {
// Back up the cwd before DesktopGameHost changes it // Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory; var cwd = Environment.CurrentDirectory;
bool useSdl = args.Contains("--sdl");
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useSdl: useSdl))
{ {
host.ExceptionThrown += handleException; host.ExceptionThrown += handleException;

View File

@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
<PackageReference Include="DiscordRichPresence" Version="1.0.121" /> <PackageReference Include="DiscordRichPresence" Version="1.0.147" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />

View File

@ -12,7 +12,7 @@
<description>click the circles. to the beat.</description> <description>click the circles. to the beat.</description>
<summary>click the circles.</summary> <summary>click the circles.</summary>
<releaseNotes>testing</releaseNotes> <releaseNotes>testing</releaseNotes>
<copyright>Copyright (c) 2019 ppy Pty Ltd</copyright> <copyright>Copyright (c) 2020 ppy Pty Ltd</copyright>
<language>en-AU</language> <language>en-AU</language>
</metadata> </metadata>
<files> <files>

View File

@ -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 System.IO;
using BenchmarkDotNet.Attributes;
using osu.Framework.IO.Stores;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
using osu.Game.IO.Archives;
using osu.Game.Resources;
namespace osu.Game.Benchmarks
{
public class BenchmarkBeatmapParsing : BenchmarkTest
{
private readonly MemoryStream beatmapStream = new MemoryStream();
public override void SetUp()
{
using (var resources = new DllResourceStore(OsuResources.ResourceAssembly))
using (var archive = resources.GetStream("Beatmaps/241526 Soleily - Renatus.osz"))
using (var reader = new ZipArchiveReader(archive))
reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream);
}
[Benchmark]
public Beatmap BenchmarkBundledBeatmap()
{
beatmapStream.Seek(0, SeekOrigin.Begin);
var reader = new LineBufferedReader(beatmapStream); // no disposal
var decoder = Decoder.GetDecoder<Beatmap>(reader);
return decoder.Decode(reader);
}
}
}

View File

@ -0,0 +1,23 @@
// 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 BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using NUnit.Framework;
namespace osu.Game.Benchmarks
{
[TestFixture]
[MemoryDiagnoser]
public abstract class BenchmarkTest
{
[GlobalSetup]
[OneTimeSetUp]
public virtual void SetUp()
{
}
[Test]
public void RunBenchmark() => BenchmarkRunner.Run(GetType());
}
}

View File

@ -0,0 +1,17 @@
// 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 BenchmarkDotNet.Running;
namespace osu.Game.Benchmarks
{
public static class Program
{
public static void Main(string[] args)
{
BenchmarkSwitcher
.FromAssembly(typeof(Program).Assembly)
.Run(args);
}
}
}

View File

@ -0,0 +1,8 @@
{
"profiles": {
"All Benchmarks": {
"commandName": "Project",
"commandLineArgs": "--filter *"
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup>
</Project>

View File

@ -36,7 +36,10 @@ namespace osu.Game.Rulesets.Catch.Mods
//disable keyboard controls //disable keyboard controls
public bool OnPressed(CatchAction action) => true; public bool OnPressed(CatchAction action) => true;
public bool OnReleased(CatchAction action) => true;
public void OnReleased(CatchAction action)
{
}
protected override bool OnMouseMove(MouseMoveEvent e) protected override bool OnMouseMove(MouseMoveEvent e)
{ {

View File

@ -36,7 +36,11 @@ namespace osu.Game.Rulesets.Catch.Objects
} }
} }
public double EndTime => StartTime + Duration; public double EndTime
{
get => StartTime + Duration;
set => Duration = value - StartTime;
}
public double Duration { get; set; } public double Duration { get; set; }
} }

View File

@ -110,7 +110,11 @@ namespace osu.Game.Rulesets.Catch.Objects
} }
} }
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; public double EndTime
{
get => StartTime + this.SpanCount() * Path.Distance / Velocity;
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
}
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;

View File

@ -20,7 +20,9 @@ namespace osu.Game.Rulesets.Catch.UI
internal readonly CatcherArea CatcherArea; internal readonly CatcherArea CatcherArea;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || CatcherArea.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
// only check the X position; handle all vertical space.
base.ReceivePositionalInputAt(new Vector2(screenSpacePos.X, ScreenSpaceDrawQuad.Centre.Y));
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation) public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation)
{ {

View File

@ -103,7 +103,9 @@ namespace osu.Game.Rulesets.Catch.UI
MovableCatcher.X = state.CatcherX.Value; MovableCatcher.X = state.CatcherX.Value;
} }
public bool OnReleased(CatchAction action) => false; public void OnReleased(CatchAction action)
{
}
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
@ -341,24 +343,22 @@ namespace osu.Game.Rulesets.Catch.UI
return false; return false;
} }
public bool OnReleased(CatchAction action) public void OnReleased(CatchAction action)
{ {
switch (action) switch (action)
{ {
case CatchAction.MoveLeft: case CatchAction.MoveLeft:
currentDirection++; currentDirection++;
return true; break;
case CatchAction.MoveRight: case CatchAction.MoveRight:
currentDirection--; currentDirection--;
return true; break;
case CatchAction.Dash: case CatchAction.Dash:
Dashing = false; Dashing = false;
return true; break;
} }
return false;
} }
/// <summary> /// <summary>

View File

@ -54,10 +54,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return true; return true;
} }
protected override bool OnMouseUp(MouseUpEvent e) protected override void OnMouseUp(MouseUpEvent e)
{ {
EndPlacement(); EndPlacement();
return base.OnMouseUp(e); base.OnMouseUp(e);
} }
public override void UpdatePosition(Vector2 screenSpacePosition) public override void UpdatePosition(Vector2 screenSpacePosition)

View File

@ -13,7 +13,7 @@ using osuTK;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints namespace osu.Game.Rulesets.Mania.Edit.Blueprints
{ {
public class ManiaSelectionBlueprint : SelectionBlueprint public class ManiaSelectionBlueprint : OverlaySelectionBlueprint
{ {
public Vector2 ScreenSpaceDragPosition { get; private set; } public Vector2 ScreenSpaceDragPosition { get; private set; }
public Vector2 DragPosition { get; private set; } public Vector2 DragPosition { get; private set; }
@ -55,14 +55,12 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return base.OnMouseDown(e); return base.OnMouseDown(e);
} }
protected override bool OnDrag(DragEvent e) protected override void OnDrag(DragEvent e)
{ {
var result = base.OnDrag(e); base.OnDrag(e);
ScreenSpaceDragPosition = e.ScreenSpaceMousePosition; ScreenSpaceDragPosition = e.ScreenSpaceMousePosition;
DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition); DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition);
return result;
} }
public override void Show() public override void Show()

View File

@ -0,0 +1,34 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Screens.Edit.Compose.Components;
namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaBlueprintContainer : ComposeBlueprintContainer
{
public ManiaBlueprintContainer(IEnumerable<DrawableHitObject> drawableHitObjects)
: base(drawableHitObjects)
{
}
public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableNote note:
return new NoteSelectionBlueprint(note);
case DrawableHoldNote holdNote:
return new HoldNoteSelectionBlueprint(holdNote);
}
return base.CreateBlueprintFor(hitObject);
}
}
}

View File

@ -5,11 +5,8 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
@ -52,26 +49,12 @@ namespace osu.Game.Rulesets.Mania.Edit
return drawableRuleset; return drawableRuleset;
} }
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ManiaBlueprintContainer(drawableRuleset.Playfield.AllHitObjects);
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[] protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
{ {
new NoteCompositionTool(), new NoteCompositionTool(),
new HoldNoteCompositionTool() new HoldNoteCompositionTool()
}; };
public override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler();
public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableNote note:
return new NoteSelectionBlueprint(note);
case DrawableHoldNote holdNote:
return new HoldNoteSelectionBlueprint(holdNote);
}
return base.CreateBlueprintFor(hitObject);
}
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
@ -70,10 +71,12 @@ namespace osu.Game.Rulesets.Mania.Edit
// When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen. // When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen.
// This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height. // This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height.
if (scrollingInfo.Direction.Value == ScrollingDirection.Down) if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
delta -= moveEvent.Blueprint.DrawableObject.Parent.DrawHeight; delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong
foreach (var b in SelectedBlueprints) foreach (var selectionBlueprint in SelectedBlueprints)
{ {
var b = (OverlaySelectionBlueprint)selectionBlueprint;
var hitObject = b.DrawableObject; var hitObject = b.DrawableObject;
var objectParent = (HitObjectContainer)hitObject.Parent; var objectParent = (HitObjectContainer)hitObject.Parent;

View File

@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Edit.Masks namespace osu.Game.Rulesets.Mania.Edit.Masks
{ {
public abstract class ManiaSelectionBlueprint : SelectionBlueprint public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
{ {
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject) protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject) : base(drawableObject)

View File

@ -237,19 +237,19 @@ namespace osu.Game.Rulesets.Mania
{ {
LeftKeys = new[] LeftKeys = new[]
{ {
InputKey.Number1, InputKey.Q,
InputKey.Number2, InputKey.W,
InputKey.Number3, InputKey.E,
InputKey.Number4, InputKey.R,
}, },
RightKeys = new[] RightKeys = new[]
{ {
InputKey.Z,
InputKey.X, InputKey.X,
InputKey.C, InputKey.C,
InputKey.V InputKey.V,
InputKey.B
}, },
SpecialKey = InputKey.Tilde, SpecialKey = InputKey.S,
SpecialAction = ManiaAction.Special1, SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1 NormalActionStart = ManiaAction.Key1
}.GenerateKeyBindingsFor(keys, out var nextNormal); }.GenerateKeyBindingsFor(keys, out var nextNormal);
@ -265,12 +265,12 @@ namespace osu.Game.Rulesets.Mania
}, },
RightKeys = new[] RightKeys = new[]
{ {
InputKey.O, InputKey.K,
InputKey.P, InputKey.L,
InputKey.BracketLeft, InputKey.Semicolon,
InputKey.BracketRight InputKey.Quote
}, },
SpecialKey = InputKey.BackSlash, SpecialKey = InputKey.I,
SpecialAction = ManiaAction.Special2, SpecialAction = ManiaAction.Special2,
NormalActionStart = nextNormal NormalActionStart = nextNormal
}.GenerateKeyBindingsFor(keys, out _); }.GenerateKeyBindingsFor(keys, out _);

View File

@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public override string Name => "Fade In"; public override string Name => "Fade In";
public override string Acronym => "FI"; public override string Acronym => "FI";
public override IconUsage Icon => OsuIcon.ModHidden; public override IconUsage? Icon => OsuIcon.ModHidden;
public override ModType Type => ModType.DifficultyIncrease; public override ModType Type => ModType.DifficultyIncrease;
public override string Description => @"Keys appear out of nowhere!"; public override string Description => @"Keys appear out of nowhere!";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Random"; public override string Name => "Random";
public override string Acronym => "RD"; public override string Acronym => "RD";
public override ModType Type => ModType.Conversion; public override ModType Type => ModType.Conversion;
public override IconUsage Icon => OsuIcon.Dice; public override IconUsage? Icon => OsuIcon.Dice;
public override string Description => @"Shuffle around the keys!"; public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;

View File

@ -171,17 +171,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
bodyPiece.Hitting = true; bodyPiece.Hitting = true;
} }
public bool OnReleased(ManiaAction action) public void OnReleased(ManiaAction action)
{ {
if (AllJudged) if (AllJudged)
return false; return;
if (action != Action.Value) if (action != Action.Value)
return false; return;
// Make sure a hold was started // Make sure a hold was started
if (HoldStartTime == null) if (HoldStartTime == null)
return false; return;
Tail.UpdateResult(); Tail.UpdateResult();
endHold(); endHold();
@ -189,8 +189,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// If the key has been released too early, the user should not receive full score for the release // If the key has been released too early, the user should not receive full score for the release
if (!Tail.IsHit) if (!Tail.IsHit)
HasBroken = true; HasBroken = true;
return true;
} }
private void endHold() private void endHold()

View File

@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
public override bool OnReleased(ManiaAction action) => false; // Handled by the hold note public override void OnReleased(ManiaAction action)
{
}
} }
} }

View File

@ -59,6 +59,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
public override bool OnReleased(ManiaAction action) => false; // Handled by the hold note public override void OnReleased(ManiaAction action)
{
}
} }
} }

View File

@ -77,6 +77,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return UpdateResult(true); return UpdateResult(true);
} }
public virtual bool OnReleased(ManiaAction action) => false; public virtual void OnReleased(ManiaAction action)
{
}
} }
} }

View File

@ -15,7 +15,11 @@ namespace osu.Game.Rulesets.Mania.Objects
/// </summary> /// </summary>
public class HoldNote : ManiaHitObject, IHasEndTime public class HoldNote : ManiaHitObject, IHasEndTime
{ {
public double EndTime => StartTime + Duration; public double EndTime
{
get => StartTime + Duration;
set => Duration = value - StartTime;
}
private double duration; private double duration;

View File

@ -191,7 +191,9 @@ namespace osu.Game.Rulesets.Mania.UI
return true; return true;
} }
public bool OnReleased(ManiaAction action) => false; public void OnReleased(ManiaAction action)
{
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border // This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border

View File

@ -98,11 +98,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
return false; return false;
} }
public bool OnReleased(ManiaAction action) public void OnReleased(ManiaAction action)
{ {
if (action == this.action.Value) if (action == this.action.Value)
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint); backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
return false;
} }
} }
} }

View File

@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{ {
public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
{ {
private const float hit_target_bar_height = 2;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>(); private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Drawable hitTarget; private readonly Drawable hitTarget;
@ -67,6 +65,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components
private class DefaultHitTarget : CompositeDrawable, IHasAccentColour private class DefaultHitTarget : CompositeDrawable, IHasAccentColour
{ {
private const float hit_target_bar_height = 2;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>(); private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Container hitTargetLine; private readonly Container hitTargetLine;

View File

@ -115,11 +115,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
return false; return false;
} }
public bool OnReleased(ManiaAction action) public void OnReleased(ManiaAction action)
{ {
if (action == this.action.Value) if (action == this.action.Value)
keyIcon.ScaleTo(1f, 125, Easing.OutQuint); keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
return false;
} }
} }
} }

View File

@ -7,12 +7,10 @@ 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;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
@ -21,7 +19,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneLegacyBeatmapSkin : OsuTestScene public class TestSceneLegacyBeatmapSkin : ScreenTestScene
{ {
[Resolved] [Resolved]
private AudioManager audio { get; set; } private AudioManager audio { get; set; }
@ -65,7 +63,8 @@ namespace osu.Game.Rulesets.Osu.Tests
ExposedPlayer player; ExposedPlayer player;
Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours); Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours);
Child = new OsuScreenStack(player = new ExposedPlayer(userHasCustomColours)) { RelativeSizeAxes = Axes.Both };
LoadScreen(player = new ExposedPlayer(userHasCustomColours));
return player; return player;
} }
@ -90,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public IReadOnlyList<Color4> UsableComboColours => public IReadOnlyList<Color4> UsableComboColours =>
GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>() GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>()
.First() .First()
.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value; .GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value;
} }
private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap

View File

@ -44,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private const double time_during_slide_2 = 3000; private const double time_during_slide_2 = 3000;
private const double time_during_slide_3 = 3500; private const double time_during_slide_3 = 3500;
private const double time_during_slide_4 = 3800; private const double time_during_slide_4 = 3800;
private const double time_slider_end = 4000;
private List<JudgementResult> judgementResults; private List<JudgementResult> judgementResults;
private bool allJudgedFired; private bool allJudgedFired;
@ -284,6 +285,48 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("Tracking acquired", assertMidSliderJudgements); AddAssert("Tracking acquired", assertMidSliderJudgements);
} }
/// <summary>
/// Scenario:
/// - Press a key on the slider head
/// - While holding the key, move cursor close to the edge of tracking area
/// - Keep the cursor on the edge of tracking area until the slider ends
/// Expected Result:
/// A passing test case will have the slider track the cursor throughout the whole test.
/// </summary>
[Test]
public void TestTrackingAreaEdge()
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
new OsuReplayFrame { Position = new Vector2(0, OsuHitObject.OBJECT_RADIUS * 1.19f), Actions = { OsuAction.LeftButton }, Time = time_slider_start + 250 },
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.199f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
});
AddAssert("Tracking kept", assertGreatJudge);
}
/// <summary>
/// Scenario:
/// - Press a key on the slider head
/// - While holding the key, move cursor just outside the tracking area
/// - Keep the cursor just outside the tracking area until the slider ends
/// Expected Result:
/// A passing test case will have the slider drop the tracking on frame 2.
/// </summary>
[Test]
public void TestTrackingAreaOutsideEdge()
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
new OsuReplayFrame { Position = new Vector2(0, OsuHitObject.OBJECT_RADIUS * 1.21f), Actions = { OsuAction.LeftButton }, Time = time_slider_start + 250 },
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.201f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
});
AddAssert("Tracking dropped", assertMidSliderJudgementFail);
}
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great; private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss; private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
@ -294,6 +337,8 @@ namespace osu.Game.Rulesets.Osu.Tests
private ScoreAccessibleReplayPlayer currentPlayer; private ScoreAccessibleReplayPlayer currentPlayer;
private const float slider_path_length = 25;
private void performTest(List<ReplayFrame> frames) private void performTest(List<ReplayFrame> frames)
{ {
AddStep("load player", () => AddStep("load player", () =>
@ -309,8 +354,8 @@ namespace osu.Game.Rulesets.Osu.Tests
Path = new SliderPath(PathType.PerfectCurve, new[] Path = new SliderPath(PathType.PerfectCurve, new[]
{ {
Vector2.Zero, Vector2.Zero,
new Vector2(25, 0), new Vector2(slider_path_length, 0),
}, 25), }, slider_path_length),
} }
}, },
BeatmapInfo = BeatmapInfo =

View File

@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -7,10 +7,10 @@ using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints namespace osu.Game.Rulesets.Osu.Edit.Blueprints
{ {
public abstract class OsuSelectionBlueprint<T> : SelectionBlueprint public abstract class OsuSelectionBlueprint<T> : OverlaySelectionBlueprint
where T : OsuHitObject where T : OsuHitObject
{ {
protected T HitObject => (T)DrawableObject.HitObject; protected new T HitObject => (T)DrawableObject.HitObject;
protected OsuSelectionBlueprint(DrawableHitObject drawableObject) protected OsuSelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject) : base(drawableObject)

View File

@ -135,13 +135,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return false; return false;
} }
protected override bool OnMouseUp(MouseUpEvent e) => RequestSelection != null;
protected override bool OnClick(ClickEvent e) => RequestSelection != null; protected override bool OnClick(ClickEvent e) => RequestSelection != null;
protected override bool OnDragStart(DragStartEvent e) => e.Button == MouseButton.Left; protected override bool OnDragStart(DragStartEvent e) => e.Button == MouseButton.Left;
protected override bool OnDrag(DragEvent e) protected override void OnDrag(DragEvent e)
{ {
if (ControlPoint == slider.Path.ControlPoints[0]) if (ControlPoint == slider.Path.ControlPoints[0])
{ {
@ -158,12 +156,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
} }
else else
ControlPoint.Position.Value += e.Delta; ControlPoint.Position.Value += e.Delta;
return true;
} }
protected override bool OnDragEnd(DragEndEvent e) => true;
/// <summary> /// <summary>
/// Updates the state of the circular control point marker. /// Updates the state of the circular control point marker.
/// </summary> /// </summary>

View File

@ -108,7 +108,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return false; return false;
} }
public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete; public void OnReleased(PlatformAction action)
{
}
private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e) private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
{ {

View File

@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
: base(slider) : base(slider)
{ {
this.position = position; this.position = position;
InternalChild = CirclePiece = new HitCirclePiece(); InternalChild = CirclePiece = new HitCirclePiece();
Select(); Select();

View File

@ -106,11 +106,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
return true; return true;
} }
protected override bool OnMouseUp(MouseUpEvent e) protected override void OnMouseUp(MouseUpEvent e)
{ {
if (state == PlacementState.Body && e.Button == MouseButton.Right) if (state == PlacementState.Body && e.Button == MouseButton.Right)
endCurve(); endCurve();
return base.OnMouseUp(e); base.OnMouseUp(e);
} }
protected override bool OnDoubleClick(DoubleClickEvent e) protected override bool OnDoubleClick(DoubleClickEvent e)

View File

@ -90,19 +90,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
protected override bool OnDragStart(DragStartEvent e) => placementControlPointIndex != null; protected override bool OnDragStart(DragStartEvent e) => placementControlPointIndex != null;
protected override bool OnDrag(DragEvent e) protected override void OnDrag(DragEvent e)
{ {
Debug.Assert(placementControlPointIndex != null); Debug.Assert(placementControlPointIndex != null);
HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position; HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position;
return true;
} }
protected override bool OnDragEnd(DragEndEvent e) protected override void OnDragEnd(DragEndEvent e)
{ {
placementControlPointIndex = null; placementControlPointIndex = null;
return true;
} }
private BindableList<PathControlPoint> controlPoints => HitObject.Path.ControlPoints; private BindableList<PathControlPoint> controlPoints => HitObject.Path.ControlPoints;
@ -173,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)), new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
}; };
public override Vector2 SelectionPoint => HeadBlueprint.SelectionPoint; public override Vector2 SelectionPoint => ((DrawableSlider)DrawableObject).HeadCircle.ScreenSpaceDrawQuad.Centre;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos);

View File

@ -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;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
@ -8,6 +9,7 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osuTK; using osuTK;
using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
{ {
@ -29,22 +31,31 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
{ {
base.Update(); base.Update();
if (isPlacingEnd)
HitObject.EndTime = Math.Max(HitObject.StartTime, EditorClock.CurrentTime);
piece.UpdateFrom(HitObject); piece.UpdateFrom(HitObject);
} }
protected override bool OnClick(ClickEvent e) protected override bool OnMouseDown(MouseDownEvent e)
{ {
if (isPlacingEnd) if (isPlacingEnd)
{ {
if (e.Button != MouseButton.Right)
return false;
HitObject.EndTime = EditorClock.CurrentTime; HitObject.EndTime = EditorClock.CurrentTime;
EndPlacement(); EndPlacement();
} }
else else
{ {
isPlacingEnd = true; if (e.Button != MouseButton.Left)
piece.FadeTo(1f, 150, Easing.OutQuint); return false;
BeginPlacement(); BeginPlacement();
piece.FadeTo(1f, 150, Easing.OutQuint);
isPlacingEnd = true;
} }
return true; return true;

View File

@ -0,0 +1,41 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Edit.Compose.Components;
namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuBlueprintContainer : ComposeBlueprintContainer
{
public OsuBlueprintContainer(IEnumerable<DrawableHitObject> drawableHitObjects)
: base(drawableHitObjects)
{
}
protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableHitCircle circle:
return new HitCircleSelectionBlueprint(circle);
case DrawableSlider slider:
return new SliderSelectionBlueprint(slider);
case DrawableSpinner spinner:
return new SpinnerSelectionBlueprint(spinner);
}
return base.CreateBlueprintFor(hitObject);
}
}
}

View File

@ -9,12 +9,7 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components;
@ -37,27 +32,13 @@ namespace osu.Game.Rulesets.Osu.Edit
new SpinnerCompositionTool() new SpinnerCompositionTool()
}; };
public override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler(); protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects);
public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableHitCircle circle:
return new HitCircleSelectionBlueprint(circle);
case DrawableSlider slider:
return new SliderSelectionBlueprint(slider);
case DrawableSpinner spinner:
return new SpinnerSelectionBlueprint(spinner);
}
return base.CreateBlueprintFor(hitObject);
}
protected override DistanceSnapGrid CreateDistanceSnapGrid(IEnumerable<HitObject> selectedHitObjects) protected override DistanceSnapGrid CreateDistanceSnapGrid(IEnumerable<HitObject> selectedHitObjects)
{ {
if (BlueprintContainer.CurrentTool is SpinnerCompositionTool)
return null;
var objects = selectedHitObjects.ToList(); var objects = selectedHitObjects.ToList();
if (objects.Count == 0) if (objects.Count == 0)
@ -111,6 +92,9 @@ namespace osu.Game.Rulesets.Osu.Edit
targetIndex++; targetIndex++;
} }
if (sourceObject is Spinner)
return null;
return new OsuDistanceSnapGrid((OsuHitObject)sourceObject, (OsuHitObject)targetObject); return new OsuDistanceSnapGrid((OsuHitObject)sourceObject, (OsuHitObject)targetObject);
} }
} }

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public override string Name => "Autopilot"; public override string Name => "Autopilot";
public override string Acronym => "AP"; public override string Acronym => "AP";
public override IconUsage Icon => OsuIcon.ModAutopilot; public override IconUsage? Icon => OsuIcon.ModAutopilot;
public override ModType Type => ModType.Automation; public override ModType Type => ModType.Automation;
public override string Description => @"Automatic cursor movement - just follow the rhythm."; public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Description => "Play with blinds on your screen."; public override string Description => "Play with blinds on your screen.";
public override string Acronym => "BL"; public override string Acronym => "BL";
public override IconUsage Icon => FontAwesome.Solid.Adjust; public override IconUsage? Icon => FontAwesome.Solid.Adjust;
public override ModType Type => ModType.DifficultyIncrease; public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => false; public override bool Ranked => false;

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Acronym => "DF"; public override string Acronym => "DF";
public override IconUsage Icon => FontAwesome.Solid.CompressArrowsAlt; public override IconUsage? Icon => FontAwesome.Solid.CompressArrowsAlt;
public override string Description => "Hit them at the right size!"; public override string Description => "Hit them at the right size!";

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Acronym => "GR"; public override string Acronym => "GR";
public override IconUsage Icon => FontAwesome.Solid.ArrowsAltV; public override IconUsage? Icon => FontAwesome.Solid.ArrowsAltV;
public override string Description => "Hit them at the right size!"; public override string Description => "Hit them at the right size!";

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public override string Name => "Spin In"; public override string Name => "Spin In";
public override string Acronym => "SI"; public override string Acronym => "SI";
public override IconUsage Icon => FontAwesome.Solid.Undo; public override IconUsage? Icon => FontAwesome.Solid.Undo;
public override ModType Type => ModType.Fun; public override ModType Type => ModType.Fun;
public override string Description => "Circles spin in. No approach circles."; public override string Description => "Circles spin in. No approach circles.";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public override string Name => "Spun Out"; public override string Name => "Spun Out";
public override string Acronym => "SO"; public override string Acronym => "SO";
public override IconUsage Icon => OsuIcon.ModSpunout; public override IconUsage? Icon => OsuIcon.ModSpunout;
public override ModType Type => ModType.DifficultyReduction; public override ModType Type => ModType.DifficultyReduction;
public override string Description => @"Spinners will be automatically completed."; public override string Description => @"Spinners will be automatically completed.";
public override double ScoreMultiplier => 0.9; public override double ScoreMultiplier => 0.9;

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Name => "Target"; public override string Name => "Target";
public override string Acronym => "TP"; public override string Acronym => "TP";
public override ModType Type => ModType.Conversion; public override ModType Type => ModType.Conversion;
public override IconUsage Icon => OsuIcon.ModTarget; public override IconUsage? Icon => OsuIcon.ModTarget;
public override string Description => @"Practice keeping up with the beat of the song."; public override string Description => @"Practice keeping up with the beat of the song.";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
} }

View File

@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -19,7 +18,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public override string Name => "Traceable"; public override string Name => "Traceable";
public override string Acronym => "TC"; public override string Acronym => "TC";
public override IconUsage Icon => FontAwesome.Brands.SnapchatGhost;
public override ModType Type => ModType.Fun; public override ModType Type => ModType.Fun;
public override string Description => "Put your faith in the approach circles..."; public override string Description => "Put your faith in the approach circles...";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public override string Name => "Transform"; public override string Name => "Transform";
public override string Acronym => "TR"; public override string Acronym => "TR";
public override IconUsage Icon => FontAwesome.Solid.ArrowsAlt; public override IconUsage? Icon => FontAwesome.Solid.ArrowsAlt;
public override ModType Type => ModType.Fun; public override ModType Type => ModType.Fun;
public override string Description => "Everything rotates. EVERYTHING."; public override string Description => "Everything rotates. EVERYTHING.";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public override string Name => "Wiggle"; public override string Name => "Wiggle";
public override string Acronym => "WG"; public override string Acronym => "WG";
public override IconUsage Icon => FontAwesome.Solid.Certificate; public override IconUsage? Icon => FontAwesome.Solid.Certificate;
public override ModType Type => ModType.Fun; public override ModType Type => ModType.Fun;
public override string Description => "They just won't stay still..."; public override string Description => "They just won't stay still...";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// <summary> /// <summary>
/// The start time of <see cref="Start"/>. /// The start time of <see cref="Start"/>.
/// </summary> /// </summary>
public readonly Bindable<double> StartTime = new Bindable<double>(); public readonly Bindable<double> StartTime = new BindableDouble();
/// <summary> /// <summary>
/// The <see cref="DrawableOsuHitObject"/> which <see cref="FollowPoint"/>s will exit from. /// The <see cref="DrawableOsuHitObject"/> which <see cref="FollowPoint"/>s will exit from.

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>(); private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
private readonly IBindable<int> stackHeightBindable = new Bindable<int>(); private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
private readonly IBindable<float> scaleBindable = new Bindable<float>(); private readonly IBindable<float> scaleBindable = new BindableFloat();
public OsuAction? HitAction => HitArea.HitAction; public OsuAction? HitAction => HitArea.HitAction;
@ -205,7 +205,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return false; return false;
} }
public bool OnReleased(OsuAction action) => false; public void OnReleased(OsuAction action)
{
}
} }
} }
} }

View File

@ -6,13 +6,11 @@ using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osuTK; using osuTK;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -23,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private double animDuration; private double animDuration;
private readonly SkinnableDrawable scaleContainer; private readonly Drawable scaleContainer;
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
: base(repeatPoint) : base(repeatPoint)
@ -36,19 +34,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Blending = BlendingParameters.Additive; Blending = BlendingParameters.Additive;
Origin = Anchor.Centre; Origin = Anchor.Centre;
InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon InternalChild = scaleContainer = new ReverseArrowPiece();
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Solid.ChevronRight,
Size = new Vector2(0.35f)
}, confineMode: ConfineMode.NoScaling)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
} }
private readonly IBindable<float> scaleBindable = new Bindable<float>(); private readonly IBindable<float> scaleBindable = new BindableFloat();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
@ -65,11 +54,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateInitialTransforms() protected override void UpdateInitialTransforms()
{ {
animDuration = Math.Min(150, repeatPoint.SpanDuration / 2); animDuration = Math.Min(300, repeatPoint.SpanDuration);
this.Animate( this.Animate(
d => d.FadeIn(animDuration), d => d.FadeIn(animDuration),
d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 4, Easing.OutElasticHalf) d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 2, Easing.OutElasticHalf)
); );
} }
@ -88,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break; break;
case ArmedState.Hit: case ArmedState.Hit:
this.FadeOut(animDuration, Easing.OutQuint) this.FadeOut(animDuration, Easing.Out)
.ScaleTo(Scale * 1.5f, animDuration, Easing.Out); .ScaleTo(Scale * 1.5f, animDuration, Easing.Out);
break; break;
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>(); private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
private readonly IBindable<int> stackHeightBindable = new Bindable<int>(); private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
private readonly IBindable<float> scaleBindable = new Bindable<float>(); private readonly IBindable<float> scaleBindable = new BindableFloat();
public DrawableSlider(Slider s) public DrawableSlider(Slider s)
: base(s) : base(s)

View File

@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}; };
} }
private readonly IBindable<float> scaleBindable = new Bindable<float>(); private readonly IBindable<float> scaleBindable = new BindableFloat();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()

View File

@ -0,0 +1,43 @@
// 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.Audio.Track;
using osu.Framework.Graphics;
using osuTK;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class ReverseArrowPiece : BeatSyncedContainer
{
public ReverseArrowPiece()
{
Divisor = 2;
MinimumBeatLength = 200;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Blending = BlendingParameters.Additive;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Solid.ChevronRight,
Size = new Vector2(0.35f)
})
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) =>
Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out);
}
}

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public Func<OsuAction?> GetInitialHitAction; public Func<OsuAction?> GetInitialHitAction;
private readonly Slider slider; private readonly Slider slider;
public readonly Drawable FollowCircle; private readonly Drawable followCircle;
private readonly DrawableSlider drawableSlider; private readonly DrawableSlider drawableSlider;
public SliderBall(Slider slider, DrawableSlider drawableSlider = null) public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Children = new[] Children = new[]
{ {
FollowCircle = new FollowCircleContainer followCircle = new FollowCircleContainer
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -95,8 +95,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
tracking = value; tracking = value;
FollowCircle.ScaleTo(tracking ? 2f : 1, 300, Easing.OutQuint); followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint);
FollowCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint); followCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
} }
} }
@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
// in valid time range // in valid time range
Time.Current >= slider.StartTime && Time.Current < slider.EndTime && Time.Current >= slider.StartTime && Time.Current < slider.EndTime &&
// in valid position range // in valid position range
lastScreenSpaceMousePosition.HasValue && FollowCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
// valid action // valid action
(actions?.Any(isValidTrackingAction) ?? false); (actions?.Any(isValidTrackingAction) ?? false);
} }

View File

@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double Radius => OBJECT_RADIUS * Scale; public double Radius => OBJECT_RADIUS * Scale;
public readonly Bindable<float> ScaleBindable = new Bindable<float>(1); public readonly Bindable<float> ScaleBindable = new BindableFloat(1);
public float Scale public float Scale
{ {

View File

@ -18,7 +18,12 @@ namespace osu.Game.Rulesets.Osu.Objects
{ {
public class Slider : OsuHitObject, IHasCurve public class Slider : OsuHitObject, IHasCurve
{ {
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; public double EndTime
{
get => StartTime + this.SpanCount() * Path.Distance / Velocity;
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
}
public double Duration => EndTime - StartTime; public double Duration => EndTime - StartTime;
private readonly Cached<Vector2> endPositionCache = new Cached<Vector2>(); private readonly Cached<Vector2> endPositionCache = new Cached<Vector2>();
@ -81,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.Objects
set set
{ {
repeatCount = value; repeatCount = value;
endPositionCache.Invalidate(); updateNestedPositions();
} }
} }

View File

@ -13,8 +13,13 @@ namespace osu.Game.Rulesets.Osu.Objects
{ {
public class Spinner : OsuHitObject, IHasEndTime public class Spinner : OsuHitObject, IHasEndTime
{ {
public double EndTime { get; set; } public double EndTime
public double Duration => EndTime - StartTime; {
get => StartTime + Duration;
set => Duration = value - StartTime;
}
public double Duration { get; set; }
/// <summary> /// <summary>
/// Number of spins required to finish the spinner without miss. /// Number of spins required to finish the spinner without miss.

View File

@ -46,17 +46,20 @@ namespace osu.Game.Rulesets.Osu.Skinning
switch (osuComponent.Component) switch (osuComponent.Component)
{ {
case OsuSkinComponents.FollowPoint: case OsuSkinComponents.FollowPoint:
return this.GetAnimation(component.LookupName, true, false); return this.GetAnimation(component.LookupName, true, false, true);
case OsuSkinComponents.SliderFollowCircle: case OsuSkinComponents.SliderFollowCircle:
var followCircle = this.GetAnimation("sliderfollowcircle", true, true); var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true);
if (followCircle != null) if (followCircle != null)
// follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x
followCircle.Scale *= 0.5f; followCircle.Scale *= 0.5f;
return followCircle; return followCircle;
case OsuSkinComponents.SliderBall: case OsuSkinComponents.SliderBall:
var sliderBallContent = this.GetAnimation("sliderb", true, true, ""); var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: "");
// todo: slider ball has a custom frame delay based on velocity
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
if (sliderBallContent != null) if (sliderBallContent != null)
{ {

View File

@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize); autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
autoCursorScale.ValueChanged += _ => calculateScale(); autoCursorScale.ValueChanged += _ => calculateScale();
CursorScale = new Bindable<float>(); CursorScale = new BindableFloat();
CursorScale.ValueChanged += e => ActiveCursor.Scale = cursorTrail.Scale = new Vector2(e.NewValue); CursorScale.ValueChanged += e => ActiveCursor.Scale = cursorTrail.Scale = new Vector2(e.NewValue);
calculateScale(); calculateScale();
@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
return false; return false;
} }
public bool OnReleased(OsuAction action) public void OnReleased(OsuAction action)
{ {
switch (action) switch (action)
{ {
@ -120,8 +120,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
updateExpandedState(); updateExpandedState();
break; break;
} }
return false;
} }
public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.

View File

@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.UI
{ {
Add(localCursorContainer = new OsuCursorContainer()); Add(localCursorContainer = new OsuCursorContainer());
localCursorScale = new Bindable<float>(); localCursorScale = new BindableFloat();
localCursorScale.BindTo(localCursorContainer.CursorScale); localCursorScale.BindTo(localCursorContainer.CursorScale);
localCursorScale.BindValueChanged(scale => cursorScaleContainer.Scale = new Vector2(scale.NewValue), true); localCursorScale.BindValueChanged(scale => cursorScaleContainer.Scale = new Vector2(scale.NewValue), true);
} }
@ -107,7 +107,9 @@ namespace osu.Game.Rulesets.Osu.UI
return false; return false;
} }
public bool OnReleased(OsuAction action) => false; public void OnReleased(OsuAction action)
{
}
public void Appear() => Schedule(() => public void Appear() => Schedule(() =>
{ {

View File

@ -77,11 +77,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return result; return result;
} }
public override bool OnReleased(TaikoAction action) public override void OnReleased(TaikoAction action)
{ {
if (action == HitAction) if (action == HitAction)
HitAction = null; HitAction = null;
return base.OnReleased(action);
base.OnReleased(action);
} }
protected override void Update() protected override void Update()

View File

@ -77,7 +77,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public Drawable CreateProxiedContent() => proxiedContent.CreateProxy(); public Drawable CreateProxiedContent() => proxiedContent.CreateProxy();
public abstract bool OnPressed(TaikoAction action); public abstract bool OnPressed(TaikoAction action);
public virtual bool OnReleased(TaikoAction action) => false;
public virtual void OnReleased(TaikoAction action)
{
}
public override double LifetimeStart public override double LifetimeStart
{ {

View File

@ -18,7 +18,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// </summary> /// </summary>
private const float base_distance = 100; private const float base_distance = 100;
public double EndTime => StartTime + Duration; public double EndTime
{
get => StartTime + Duration;
set => Duration = value - StartTime;
}
public double Duration { get; set; } public double Duration { get; set; }

View File

@ -11,7 +11,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
{ {
public class Swell : TaikoHitObject, IHasEndTime public class Swell : TaikoHitObject, IHasEndTime
{ {
public double EndTime => StartTime + Duration; public double EndTime
{
get => StartTime + Duration;
set => Duration = value - StartTime;
}
public double Duration { get; set; } public double Duration { get; set; }

View File

@ -187,7 +187,9 @@ namespace osu.Game.Rulesets.Taiko.UI
return false; return false;
} }
public bool OnReleased(TaikoAction action) => false; public void OnReleased(TaikoAction action)
{
}
} }
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
@ -13,7 +14,9 @@ using osu.Game.IPC;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
using SharpCompress.Archives; using SharpCompress.Archives;
using SharpCompress.Archives.Zip; using SharpCompress.Archives.Zip;
@ -552,6 +555,83 @@ namespace osu.Game.Tests.Beatmaps.IO
} }
} }
[Test]
public async Task TestUpdateBeatmapInfo()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapInfo)))
{
try
{
var osu = loadOsu(host);
var manager = osu.Dependencies.Get<BeatmapManager>();
var temp = TestResources.GetTestBeatmapForImport();
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
// Update via the beatmap, not the beatmap info, to ensure correct linking
BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap;
beatmapToUpdate.BeatmapInfo.Version = "updated";
manager.Update(setToUpdate);
BeatmapInfo updatedInfo = manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID);
Assert.That(updatedInfo.Version, Is.EqualTo("updated"));
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestUpdateBeatmapFile()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapFile)))
{
try
{
var osu = loadOsu(host);
var manager = osu.Dependencies.Get<BeatmapManager>();
var temp = TestResources.GetTestBeatmapForImport();
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap;
BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename));
using (var stream = new MemoryStream())
{
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
{
beatmapToUpdate.HitObjects.Clear();
beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 });
new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer);
}
stream.Seek(0, SeekOrigin.Begin);
manager.UpdateFile(setToUpdate, fileToUpdate, stream);
}
// Check that the old file reference has been removed
Assert.That(manager.QueryBeatmapSet(s => s.ID == setToUpdate.ID).Files.All(f => f.ID != fileToUpdate.ID));
// Check that the new file is referenced correctly by attempting a retrieval
Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap;
Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1));
Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000));
}
finally
{
host.Exit();
}
}
}
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
{ {
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);

View File

@ -3,8 +3,11 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit;
@ -19,12 +22,34 @@ namespace osu.Game.Tests.Editor
private TestHitObjectComposer composer; private TestHitObjectComposer composer;
[Cached(typeof(EditorBeatmap))] [Cached(typeof(EditorBeatmap))]
private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap()); [Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap;
protected override Container<Drawable> Content { get; }
public TestSceneHitObjectComposerDistanceSnapping()
{
base.Content.Add(new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
editorBeatmap = new EditorBeatmap(new OsuBeatmap()),
Content = new Container
{
RelativeSizeAxes = Axes.Both,
}
},
});
}
[SetUp] [SetUp]
public void Setup() => Schedule(() => public void Setup() => Schedule(() =>
{ {
Child = composer = new TestHitObjectComposer(); Children = new Drawable[]
{
composer = new TestHitObjectComposer()
};
BeatDivisor.Value = 1; BeatDivisor.Value = 1;
@ -111,17 +136,19 @@ namespace osu.Game.Tests.Editor
[Test] [Test]
public void TestGetSnappedDurationFromDistance() public void TestGetSnappedDurationFromDistance()
{ {
assertSnappedDuration(50, 0); assertSnappedDuration(0, 0);
assertSnappedDuration(50, 1000);
assertSnappedDuration(100, 1000); assertSnappedDuration(100, 1000);
assertSnappedDuration(150, 1000); assertSnappedDuration(150, 2000);
assertSnappedDuration(200, 2000); assertSnappedDuration(200, 2000);
assertSnappedDuration(250, 2000); assertSnappedDuration(250, 3000);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2); AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDuration(0, 0);
assertSnappedDuration(50, 0); assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0); assertSnappedDuration(100, 1000);
assertSnappedDuration(150, 0); assertSnappedDuration(150, 1000);
assertSnappedDuration(200, 1000); assertSnappedDuration(200, 1000);
assertSnappedDuration(250, 1000); assertSnappedDuration(250, 1000);
@ -132,8 +159,8 @@ namespace osu.Game.Tests.Editor
}); });
assertSnappedDuration(50, 0); assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0); assertSnappedDuration(100, 500);
assertSnappedDuration(150, 0); assertSnappedDuration(150, 500);
assertSnappedDuration(200, 500); assertSnappedDuration(200, 500);
assertSnappedDuration(250, 500); assertSnappedDuration(250, 500);
assertSnappedDuration(400, 1000); assertSnappedDuration(400, 1000);
@ -142,17 +169,17 @@ namespace osu.Game.Tests.Editor
[Test] [Test]
public void GetSnappedDistanceFromDistance() public void GetSnappedDistanceFromDistance()
{ {
assertSnappedDistance(50, 0); assertSnappedDistance(50, 100);
assertSnappedDistance(100, 100); assertSnappedDistance(100, 100);
assertSnappedDistance(150, 100); assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200); assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200); assertSnappedDistance(250, 300);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2); AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDistance(50, 0); assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0); assertSnappedDistance(100, 200);
assertSnappedDistance(150, 0); assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200); assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200); assertSnappedDistance(250, 200);
@ -163,8 +190,8 @@ namespace osu.Game.Tests.Editor
}); });
assertSnappedDistance(50, 0); assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0); assertSnappedDistance(100, 200);
assertSnappedDistance(150, 0); assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200); assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200); assertSnappedDistance(250, 200);
assertSnappedDistance(400, 400); assertSnappedDistance(400, 400);

View File

@ -126,10 +126,10 @@ namespace osu.Game.Tests.Gameplay
{ {
switch (lookup) switch (lookup)
{ {
case GlobalSkinConfiguration global: case GlobalSkinColours global:
switch (global) switch (global)
{ {
case GlobalSkinConfiguration.ComboColours: case GlobalSkinColours.ComboColours:
return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(ComboColours)); return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(ComboColours));
} }

View File

@ -0,0 +1,82 @@
// 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 Newtonsoft.Json;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Tests.Online
{
[TestFixture]
public class TestAPIModSerialization
{
[Test]
public void TestAcronymIsPreserved()
{
var apiMod = new APIMod(new TestMod());
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
Assert.That(deserialized.Acronym, Is.EqualTo(apiMod.Acronym));
}
[Test]
public void TestRawSettingIsPreserved()
{
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(2.0));
}
[Test]
public void TestConvertedModHasCorrectSetting()
{
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
var converted = (TestMod)deserialized.ToMod(new TestRuleset());
Assert.That(converted.TestSetting.Value, Is.EqualTo(2));
}
private class TestRuleset : Ruleset
{
public override IEnumerable<Mod> GetModsFor(ModType type) => new[] { new TestMod() };
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new System.NotImplementedException();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException();
public override string Description { get; } = string.Empty;
public override string ShortName { get; } = string.Empty;
}
private class TestMod : Mod
{
public override string Name => "Test Mod";
public override string Acronym => "TM";
public override double ScoreMultiplier => 1;
[SettingSource("Test")]
public BindableNumber<double> TestSetting { get; } = new BindableDouble
{
MinValue = 0,
MaxValue = 10,
Default = 5,
Precision = 0.01,
};
}
}
}

View File

@ -18,12 +18,21 @@ namespace osu.Game.Tests.ScrollAlgorithms
} }
[Test] [Test]
public void TestDisplayStartTime() public void TestPointDisplayStartTime()
{ {
Assert.AreEqual(-8000, algorithm.GetDisplayStartTime(2000, 10000)); Assert.AreEqual(-8000, algorithm.GetDisplayStartTime(2000, 0, 10000, 1));
Assert.AreEqual(-3000, algorithm.GetDisplayStartTime(2000, 5000)); Assert.AreEqual(-3000, algorithm.GetDisplayStartTime(2000, 0, 5000, 1));
Assert.AreEqual(2000, algorithm.GetDisplayStartTime(7000, 5000)); Assert.AreEqual(2000, algorithm.GetDisplayStartTime(7000, 0, 5000, 1));
Assert.AreEqual(7000, algorithm.GetDisplayStartTime(17000, 10000)); Assert.AreEqual(7000, algorithm.GetDisplayStartTime(17000, 0, 10000, 1));
}
[Test]
public void TestObjectDisplayStartTime()
{
Assert.AreEqual(900, algorithm.GetDisplayStartTime(2000, 50, 1000, 500)); // 2000 - (1 + 50 / 500) * 1000
Assert.AreEqual(8900, algorithm.GetDisplayStartTime(10000, 50, 1000, 500)); // 10000 - (1 + 50 / 500) * 1000
Assert.AreEqual(13500, algorithm.GetDisplayStartTime(15000, 250, 1000, 500)); // 15000 - (1 + 250 / 500) * 1000
Assert.AreEqual(19000, algorithm.GetDisplayStartTime(25000, 100, 5000, 500)); // 25000 - (1 + 100 / 500) * 5000
} }
[Test] [Test]

View File

@ -27,11 +27,22 @@ namespace osu.Game.Tests.ScrollAlgorithms
} }
[Test] [Test]
public void TestDisplayStartTime() public void TestPointDisplayStartTime()
{ {
Assert.AreEqual(1000, algorithm.GetDisplayStartTime(2000, 1000)); // Like constant Assert.AreEqual(1000, algorithm.GetDisplayStartTime(2000, 0, 1000, 1)); // Like constant
Assert.AreEqual(10000, algorithm.GetDisplayStartTime(10500, 1000)); // 10500 - (1000 * 0.5) Assert.AreEqual(10000, algorithm.GetDisplayStartTime(10500, 0, 1000, 1)); // 10500 - (1000 * 0.5)
Assert.AreEqual(20000, algorithm.GetDisplayStartTime(22000, 1000)); // 23000 - (1000 / 0.5) Assert.AreEqual(20000, algorithm.GetDisplayStartTime(22000, 0, 1000, 1)); // 23000 - (1000 / 0.5)
}
[Test]
public void TestObjectDisplayStartTime()
{
Assert.AreEqual(900, algorithm.GetDisplayStartTime(2000, 50, 1000, 500)); // 2000 - (1 + 50 / 500) * 1000 / 1
Assert.AreEqual(9450, algorithm.GetDisplayStartTime(10000, 50, 1000, 500)); // 10000 - (1 + 50 / 500) * 1000 / 2
Assert.AreEqual(14250, algorithm.GetDisplayStartTime(15000, 250, 1000, 500)); // 15000 - (1 + 250 / 500) * 1000 / 2
Assert.AreEqual(16500, algorithm.GetDisplayStartTime(18000, 250, 2000, 500)); // 18000 - (1 + 250 / 500) * 2000 / 2
Assert.AreEqual(17800, algorithm.GetDisplayStartTime(20000, 50, 1000, 500)); // 20000 - (1 + 50 / 500) * 1000 / 0.5
Assert.AreEqual(19800, algorithm.GetDisplayStartTime(22000, 50, 1000, 500)); // 22000 - (1 + 50 / 500) * 1000 / 0.5
} }
[Test] [Test]

View File

@ -95,7 +95,7 @@ namespace osu.Game.Tests.Skins
[Test] [Test]
public void TestGlobalLookup() public void TestGlobalLookup()
{ {
AddAssert("Check combo colours", () => requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0); AddAssert("Check combo colours", () => requester.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value?.Count > 0);
} }
[Test] [Test]
@ -121,7 +121,7 @@ namespace osu.Game.Tests.Skins
public void TestEmptyComboColours() public void TestEmptyComboColours()
{ {
AddAssert("Check retrieved combo colours is skin default colours", () => AddAssert("Check retrieved combo colours is skin default colours", () =>
requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.SequenceEqual(SkinConfiguration.DefaultComboColours) ?? false); requester.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value?.SequenceEqual(SkinConfiguration.DefaultComboColours) ?? false);
} }
[Test] [Test]
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Skins
AddStep("Disallow default colours fallback in source2", () => source2.Configuration.AllowDefaultComboColoursFallback = false); AddStep("Disallow default colours fallback in source2", () => source2.Configuration.AllowDefaultComboColoursFallback = false);
AddAssert("Check retrieved combo colours from source1", () => AddAssert("Check retrieved combo colours from source1", () =>
requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.SequenceEqual(source1.Configuration.ComboColours) ?? false); requester.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value?.SequenceEqual(source1.Configuration.ComboColours) ?? false);
} }
[Test] [Test]

View File

@ -68,10 +68,10 @@ namespace osu.Game.Tests.Visual.Background
[SetUp] [SetUp]
public virtual void SetUp() => Schedule(() => public virtual void SetUp() => Schedule(() =>
{ {
Child = new OsuScreenStack(songSelect = new DummySongSelect()) var stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
{ Child = stack;
RelativeSizeAxes = Axes.Both
}; stack.Push(songSelect = new DummySongSelect());
}); });
/// <summary> /// <summary>
@ -277,7 +277,7 @@ namespace osu.Game.Tests.Visual.Background
private void setupUserSettings() private void setupUserSettings()
{ {
AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null); AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null);
AddStep("Set default user settings", () => AddStep("Set default user settings", () =>
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray(); SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
@ -302,8 +302,8 @@ namespace osu.Game.Tests.Visual.Background
} }
public readonly Bindable<bool> DimEnabled = new Bindable<bool>(); public readonly Bindable<bool> DimEnabled = new Bindable<bool>();
public readonly Bindable<double> DimLevel = new Bindable<double>(); public readonly Bindable<double> DimLevel = new BindableDouble();
public readonly Bindable<double> BlurLevel = new Bindable<double>(); public readonly Bindable<double> BlurLevel = new BindableDouble();
public new BeatmapCarousel Carousel => base.Carousel; public new BeatmapCarousel Carousel => base.Carousel;

View File

@ -3,6 +3,7 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
@ -14,6 +15,7 @@ namespace osu.Game.Tests.Visual.Editor
public class TestSceneComposeScreen : EditorClockTestScene public class TestSceneComposeScreen : EditorClockTestScene
{ {
[Cached(typeof(EditorBeatmap))] [Cached(typeof(EditorBeatmap))]
[Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap = private readonly EditorBeatmap editorBeatmap =
new EditorBeatmap(new OsuBeatmap new EditorBeatmap(new OsuBeatmap
{ {

View File

@ -85,64 +85,64 @@ namespace osu.Game.Tests.Visual.Editor
{ {
} }
protected override void CreateContent(Vector2 startPosition) protected override void CreateContent()
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(5), Size = new Vector2(5),
Position = startPosition Position = StartPosition
}); });
int beatIndex = 0; int indexFromPlacement = 0;
for (float s = startPosition.X + DistanceSpacing; s <= DrawWidth && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++) for (float s = StartPosition.X + DistanceSpacing; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(5, 10), Size = new Vector2(5, 10),
Position = new Vector2(s, startPosition.Y), Position = new Vector2(s, StartPosition.Y),
Colour = GetColourForBeatIndex(beatIndex) Colour = GetColourForIndexFromPlacement(indexFromPlacement)
}); });
} }
beatIndex = 0; indexFromPlacement = 0;
for (float s = startPosition.X - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++) for (float s = StartPosition.X - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(5, 10), Size = new Vector2(5, 10),
Position = new Vector2(s, startPosition.Y), Position = new Vector2(s, StartPosition.Y),
Colour = GetColourForBeatIndex(beatIndex) Colour = GetColourForIndexFromPlacement(indexFromPlacement)
}); });
} }
beatIndex = 0; indexFromPlacement = 0;
for (float s = startPosition.Y + DistanceSpacing; s <= DrawHeight && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++) for (float s = StartPosition.Y + DistanceSpacing; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(10, 5), Size = new Vector2(10, 5),
Position = new Vector2(startPosition.X, s), Position = new Vector2(StartPosition.X, s),
Colour = GetColourForBeatIndex(beatIndex) Colour = GetColourForIndexFromPlacement(indexFromPlacement)
}); });
} }
beatIndex = 0; indexFromPlacement = 0;
for (float s = startPosition.Y - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++) for (float s = StartPosition.Y - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(10, 5), Size = new Vector2(10, 5),
Position = new Vector2(startPosition.X, s), Position = new Vector2(StartPosition.X, s),
Colour = GetColourForBeatIndex(beatIndex) Colour = GetColourForIndexFromPlacement(indexFromPlacement)
}); });
} }
} }

View File

@ -66,6 +66,7 @@ namespace osu.Game.Tests.Visual.Editor
Dependencies.CacheAs<IAdjustableClock>(clock); Dependencies.CacheAs<IAdjustableClock>(clock);
Dependencies.CacheAs<IFrameBasedClock>(clock); Dependencies.CacheAs<IFrameBasedClock>(clock);
Dependencies.CacheAs(editorBeatmap); Dependencies.CacheAs(editorBeatmap);
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
Child = new OsuHitObjectComposer(new OsuRuleset()); Child = new OsuHitObjectComposer(new OsuRuleset());
} }

View File

@ -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 System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneTimelineBlueprintContainer : TimelineTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(TimelineHitObjectBlueprint),
};
public override Drawable CreateTestComponent() => new TimelineBlueprintContainer();
protected override void LoadComplete()
{
base.LoadComplete();
Clock.Seek(10000);
}
}
}

View File

@ -0,0 +1,32 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneTimelineTickDisplay : TimelineTestScene
{
public override Drawable CreateTestComponent() => new TimelineTickDisplay();
[BackgroundDependencyLoader]
private void load()
{
BeatDivisor.Value = 4;
Add(new BeatDivisorControl(BeatDivisor)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Margin = new MarginPadding(30),
Size = new Vector2(90)
});
}
}
}

View File

@ -27,7 +27,12 @@ namespace osu.Game.Tests.Visual.Editor
}; };
[Cached(typeof(EditorBeatmap))] [Cached(typeof(EditorBeatmap))]
private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap()); private readonly EditorBeatmap editorBeatmap;
public TestSceneTimingScreen()
{
editorBeatmap = new EditorBeatmap(new OsuBeatmap());
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -13,7 +12,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Edit;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline; using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK; using osuTK;
@ -21,27 +20,33 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
{ {
[TestFixture] public abstract class TimelineTestScene : EditorClockTestScene
public class TestSceneEditorComposeTimeline : EditorClockTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
typeof(TimelineArea), typeof(TimelineArea),
typeof(TimelineHitObjectDisplay),
typeof(Timeline), typeof(Timeline),
typeof(TimelineButton), typeof(TimelineButton),
typeof(CentreMarker) typeof(CentreMarker)
}; };
protected TimelineArea TimelineArea { get; private set; }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio) private void load(AudioManager audio)
{ {
Beatmap.Value = new WaveformTestBeatmap(audio); Beatmap.Value = new WaveformTestBeatmap(audio);
var editorBeatmap = new EditorBeatmap((Beatmap<HitObject>)Beatmap.Value.Beatmap); var playable = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
Children = new Drawable[] var editorBeatmap = new EditorBeatmap(playable);
Dependencies.Cache(editorBeatmap);
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
AddRange(new Drawable[]
{ {
editorBeatmap,
new FillFlowContainer new FillFlowContainer
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
@ -53,17 +58,19 @@ namespace osu.Game.Tests.Visual.Editor
new AudioVisualiser(), new AudioVisualiser(),
} }
}, },
new TimelineArea TimelineArea = new TimelineArea
{ {
Child = new TimelineHitObjectDisplay(editorBeatmap), Child = CreateTestComponent(),
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Size = new Vector2(0.8f, 100) Size = new Vector2(0.8f, 100),
} }
}; });
} }
public abstract Drawable CreateTestComponent();
private class AudioVisualiser : CompositeDrawable private class AudioVisualiser : CompositeDrawable
{ {
private readonly Drawable marker; private readonly Drawable marker;

View File

@ -2,49 +2,66 @@
// 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.Linq; using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual.Gameplay
{ {
/// <summary> /// <summary>
/// A base class which runs <see cref="Player"/> test for all available rulesets. /// A base class which runs <see cref="Player"/> test for all available rulesets.
/// Steps to be run for each ruleset should be added via <see cref="AddCheckSteps"/>. /// Steps to be run for each ruleset should be added via <see cref="AddCheckSteps"/>.
/// </summary> /// </summary>
public abstract class AllPlayersTestScene : RateAdjustedBeatmapTestScene public abstract class TestSceneAllRulesetPlayers : RateAdjustedBeatmapTestScene
{ {
protected Player Player; protected Player Player;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(RulesetStore rulesets) private void load(RulesetStore rulesets)
{ {
foreach (var r in rulesets.AvailableRulesets)
{
Player p = null;
AddStep(r.Name, () => p = loadPlayerFor(r));
AddUntilStep("player loaded", () =>
{
if (p?.IsLoaded == true)
{
p = null;
return true;
}
return false;
});
AddCheckSteps();
}
OsuConfigManager manager; OsuConfigManager manager;
Dependencies.Cache(manager = new OsuConfigManager(LocalStorage)); Dependencies.Cache(manager = new OsuConfigManager(LocalStorage));
manager.GetBindable<double>(OsuSetting.DimLevel).Value = 1.0; manager.GetBindable<double>(OsuSetting.DimLevel).Value = 1.0;
} }
[Test]
public void TestOsu() => runForRuleset(new OsuRuleset().RulesetInfo);
[Test]
public void TestTaiko() => runForRuleset(new TaikoRuleset().RulesetInfo);
[Test]
public void TestCatch() => runForRuleset(new CatchRuleset().RulesetInfo);
[Test]
public void TestMania() => runForRuleset(new ManiaRuleset().RulesetInfo);
private void runForRuleset(RulesetInfo ruleset)
{
Player p = null;
AddStep($"load {ruleset.Name} player", () => p = loadPlayerFor(ruleset));
AddUntilStep("player loaded", () =>
{
if (p?.IsLoaded == true)
{
p = null;
return true;
}
return false;
});
AddCheckSteps();
}
protected abstract void AddCheckSteps(); protected abstract void AddCheckSteps();
private Player loadPlayerFor(RulesetInfo rulesetInfo) private Player loadPlayerFor(RulesetInfo rulesetInfo)

Some files were not shown because too many files have changed in this diff Show More