mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 14:32:55 +08:00
Merge branch 'master' into fl-opacity
This commit is contained in:
commit
36d1cdb95a
1
.idea/.idea.osu.Android/.idea/.name
Normal file
1
.idea/.idea.osu.Android/.idea/.name
Normal file
@ -0,0 +1 @@
|
|||||||
|
osu.Android
|
8
.idea/.idea.osu.Android/.idea/indexLayout.xml
Normal file
8
.idea/.idea.osu.Android/.idea/indexLayout.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/.idea.osu.Android/.idea/misc.xml
Normal file
6
.idea/.idea.osu.Android/.idea/misc.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="com.jetbrains.rider.android.RiderAndroidMiscFileCreationComponent">
|
||||||
|
<option name="ENSURE_MISC_FILE_EXISTS" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/.idea.osu.Android/.idea/projectSettingsUpdater.xml
Normal file
6
.idea/.idea.osu.Android/.idea/projectSettingsUpdater.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RiderProjectSettingsUpdater">
|
||||||
|
<option name="vcsConfiguration" value="2" />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/.idea.osu.Android/.idea/vcs.xml
Normal file
6
.idea/.idea.osu.Android/.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/.idea.osu/.idea/misc.xml
Normal file
6
.idea/.idea.osu/.idea/misc.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="com.jetbrains.rider.android.RiderAndroidMiscFileCreationComponent">
|
||||||
|
<option name="ENSURE_MISC_FILE_EXISTS" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.osu/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.osu/riderModule.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="RiderProjectSettingsUpdater">
|
<component name="RiderProjectSettingsUpdater">
|
||||||
<option name="vcsConfiguration" value="1" />
|
<option name="vcsConfiguration" value="2" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -13,3 +13,5 @@ M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.H
|
|||||||
M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection<T>,NotificationCallbackDelegate<T>) instead.
|
M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection<T>,NotificationCallbackDelegate<T>) instead.
|
||||||
M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable<T>,NotificationCallbackDelegate<T>) instead.
|
M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable<T>,NotificationCallbackDelegate<T>) instead.
|
||||||
M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList<T>,NotificationCallbackDelegate<T>) instead.
|
M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList<T>,NotificationCallbackDelegate<T>) instead.
|
||||||
|
M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks.
|
||||||
|
P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks.
|
||||||
|
@ -31,7 +31,7 @@ If you are looking to install or test osu! without setting up a development envi
|
|||||||
|
|
||||||
**Latest build:**
|
**Latest build:**
|
||||||
|
|
||||||
| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
|
| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.15+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
|
||||||
| ------------- | ------------- | ------------- | ------------- | ------------- |
|
| ------------- | ------------- | ------------- | ------------- | ------------- |
|
||||||
|
|
||||||
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.
|
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Platform;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests
|
|||||||
public class TestSceneOsuGame : OsuTestScene
|
public class TestSceneOsuGame : OsuTestScene
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, OsuGameBase gameBase)
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Platform;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
|||||||
public class TestSceneOsuGame : OsuTestScene
|
public class TestSceneOsuGame : OsuTestScene
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, OsuGameBase gameBase)
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Platform;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests
|
|||||||
public class TestSceneOsuGame : OsuTestScene
|
public class TestSceneOsuGame : OsuTestScene
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, OsuGameBase gameBase)
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Platform;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
|||||||
public class TestSceneOsuGame : OsuTestScene
|
public class TestSceneOsuGame : OsuTestScene
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, OsuGameBase gameBase)
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Textures;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -28,7 +27,7 @@ namespace osu.Game.Rulesets.Pippidon.UI
|
|||||||
private PippidonCharacter pippidon;
|
private PippidonCharacter pippidon;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(TextureStore textures)
|
private void load()
|
||||||
{
|
{
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -51,11 +51,11 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1215.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.115.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1220.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.118.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
<PackageReference Include="Realm" Version="10.7.1" />
|
<PackageReference Include="Realm" Version="10.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -17,7 +17,7 @@ using osu.Game.Database;
|
|||||||
|
|
||||||
namespace osu.Android
|
namespace osu.Android
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
[Activity(ConfigurationChanges = DEFAULT_CONFIG_CHANGES, Exported = true, LaunchMode = DEFAULT_LAUNCH_MODE, MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser)]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osr", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osr", DataHost = "*", DataMimeType = "*/*")]
|
||||||
@ -47,11 +47,6 @@ namespace osu.Android
|
|||||||
|
|
||||||
protected override void OnCreate(Bundle savedInstanceState)
|
protected override void OnCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
// The default current directory on android is '/'.
|
|
||||||
// On some devices '/' maps to the app data directory. On others it maps to the root of the internal storage.
|
|
||||||
// In order to have a consistent current directory on all devices the full path of the app data directory is set as the current directory.
|
|
||||||
System.Environment.CurrentDirectory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
|
|
||||||
|
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
|
|
||||||
// OnNewIntent() only fires for an activity if it's *re-launched* while it's on top of the activity stack.
|
// OnNewIntent() only fires for an activity if it's *re-launched* while it's on top of the activity stack.
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" package="sh.ppy.osulazer" android:installLocation="auto" android:versionName="0.1.0">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" package="sh.ppy.osulazer" android:installLocation="auto" android:versionName="0.1.0">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
||||||
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<uses-permission android:name="android.permission.BATTERY_STATS" />
|
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
|
||||||
</manifest>
|
</manifest>
|
8
osu.Android/Properties/AssemblyInfo.cs
Normal file
8
osu.Android/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using Android;
|
||||||
|
using Android.App;
|
||||||
|
|
||||||
|
// used for AndroidBatteryInfo
|
||||||
|
[assembly: UsesPermission(Manifest.Permission.BatteryStats)]
|
@ -29,6 +29,7 @@
|
|||||||
<Compile Include="GameplayScreenRotationLocker.cs" />
|
<Compile Include="GameplayScreenRotationLocker.cs" />
|
||||||
<Compile Include="OsuGameActivity.cs" />
|
<Compile Include="OsuGameActivity.cs" />
|
||||||
<Compile Include="OsuGameAndroid.cs" />
|
<Compile Include="OsuGameAndroid.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Properties\AndroidManifest.xml" />
|
<None Include="Properties\AndroidManifest.xml" />
|
||||||
|
@ -10,14 +10,11 @@ using System.Runtime.Versioning;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using osu.Desktop.Security;
|
using osu.Desktop.Security;
|
||||||
using osu.Desktop.Overlays;
|
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Desktop.Updater;
|
using osu.Desktop.Updater;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
|
||||||
using osu.Game.Screens.Menu;
|
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
using osu.Desktop.Windows;
|
using osu.Desktop.Windows;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
@ -27,13 +24,9 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
internal class OsuGameDesktop : OsuGame
|
internal class OsuGameDesktop : OsuGame
|
||||||
{
|
{
|
||||||
private readonly bool noVersionOverlay;
|
|
||||||
private VersionManager versionManager;
|
|
||||||
|
|
||||||
public OsuGameDesktop(string[] args = null)
|
public OsuGameDesktop(string[] args = null)
|
||||||
: base(args)
|
: base(args)
|
||||||
{
|
{
|
||||||
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override StableStorage GetStorageForStableInstall()
|
public override StableStorage GetStorageForStableInstall()
|
||||||
@ -114,9 +107,6 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
if (!noVersionOverlay)
|
|
||||||
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, ScreenContainer.Add);
|
|
||||||
|
|
||||||
LoadComponentAsync(new DiscordRichPresence(), Add);
|
LoadComponentAsync(new DiscordRichPresence(), Add);
|
||||||
|
|
||||||
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
|
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
|
||||||
@ -125,23 +115,6 @@ namespace osu.Desktop
|
|||||||
LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
|
LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
|
|
||||||
{
|
|
||||||
base.ScreenChanged(lastScreen, newScreen);
|
|
||||||
|
|
||||||
switch (newScreen)
|
|
||||||
{
|
|
||||||
case IntroScreen _:
|
|
||||||
case MainMenu _:
|
|
||||||
versionManager?.Show();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
versionManager?.Hide();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetHost(GameHost host)
|
public override void SetHost(GameHost host)
|
||||||
{
|
{
|
||||||
base.SetHost(host);
|
base.SetHost(host);
|
||||||
|
@ -73,10 +73,10 @@ namespace osu.Desktop.Security
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, NotificationOverlay notificationOverlay)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
Icon = FontAwesome.Solid.ShieldAlt;
|
Icon = FontAwesome.Solid.ShieldAlt;
|
||||||
IconBackgound.Colour = colours.YellowDark;
|
IconBackground.Colour = colours.YellowDark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// ReSharper disable IdentifierTypo
|
||||||
|
|
||||||
namespace osu.Desktop.Windows
|
namespace osu.Desktop.Windows
|
||||||
{
|
{
|
||||||
internal class WindowsKey
|
internal class WindowsKey
|
||||||
|
@ -2,13 +2,12 @@
|
|||||||
// 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 Android.App;
|
using Android.App;
|
||||||
using Android.Content.PM;
|
|
||||||
using osu.Framework.Android;
|
using osu.Framework.Android;
|
||||||
using osu.Game.Tests;
|
using osu.Game.Tests;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests.Android
|
namespace osu.Game.Rulesets.Catch.Tests.Android
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
|
[Activity(ConfigurationChanges = DEFAULT_CONFIG_CHANGES, Exported = true, LaunchMode = DEFAULT_LAUNCH_MODE, MainLauncher = true)]
|
||||||
public class MainActivity : AndroidGameActivity
|
public class MainActivity : AndroidGameActivity
|
||||||
{
|
{
|
||||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||||
|
@ -14,7 +14,6 @@ using osu.Game.Tests.Beatmaps;
|
|||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[Timeout(10000)]
|
|
||||||
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||||
|
@ -29,7 +29,13 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
|
|
||||||
protected CatchSelectionBlueprintTestScene()
|
protected CatchSelectionBlueprintTestScene()
|
||||||
{
|
{
|
||||||
EditorBeatmap = new EditorBeatmap(new CatchBeatmap()) { Difficulty = { CircleSize = 0 } };
|
EditorBeatmap = new EditorBeatmap(new CatchBeatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Ruleset = new CatchRuleset().RulesetInfo,
|
||||||
|
}
|
||||||
|
}) { Difficulty = { CircleSize = 0 } };
|
||||||
EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint
|
EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint
|
||||||
{
|
{
|
||||||
BeatLength = 100
|
BeatLength = 100
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
|
Difficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
|
||||||
Ruleset = ruleset
|
Ruleset = ruleset
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
|
Difficulty = new BeatmapDifficulty { CircleSize = 6 },
|
||||||
Ruleset = ruleset
|
Ruleset = ruleset
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
|
Difficulty = new BeatmapDifficulty { CircleSize = 6 },
|
||||||
Ruleset = ruleset
|
Ruleset = ruleset
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -35,12 +35,12 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
HitObjects = new List<HitObject> { new Fruit() },
|
HitObjects = new List<HitObject> { new Fruit() },
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty(),
|
Difficulty = new BeatmapDifficulty(),
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Artist = @"Unknown",
|
Artist = @"Unknown",
|
||||||
Title = @"You're breathtaking",
|
Title = @"You're breathtaking",
|
||||||
AuthorString = @"Everyone",
|
Author = { Username = @"Everyone" },
|
||||||
},
|
},
|
||||||
Ruleset = new CatchRuleset().RulesetInfo
|
Ruleset = new CatchRuleset().RulesetInfo
|
||||||
},
|
},
|
||||||
@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestJuicestream()
|
public void TestJuiceStream()
|
||||||
{
|
{
|
||||||
AddStep("hit juicestream", () => spawnJuiceStream(true));
|
AddStep("hit juicestream", () => spawnJuiceStream(true));
|
||||||
AddUntilStep("wait for completion", () => playfieldIsEmpty);
|
AddUntilStep("wait for completion", () => playfieldIsEmpty);
|
||||||
|
@ -37,20 +37,20 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
AddStep("show hyperdash droplet", () => SetContents(_ => createDrawableDroplet(true)));
|
AddStep("show hyperdash droplet", () => SetContents(_ => createDrawableDroplet(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>
|
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperDash = false) =>
|
||||||
new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
||||||
{
|
{
|
||||||
IndexInBeatmap = indexInBeatmap,
|
IndexInBeatmap = indexInBeatmap,
|
||||||
HyperDashBindable = { Value = hyperdash }
|
HyperDashBindable = { Value = hyperDash }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
private Drawable createDrawableBanana() =>
|
private Drawable createDrawableBanana() =>
|
||||||
new TestDrawableCatchHitObjectSpecimen(new DrawableBanana(new Banana()));
|
new TestDrawableCatchHitObjectSpecimen(new DrawableBanana(new Banana()));
|
||||||
|
|
||||||
private Drawable createDrawableDroplet(bool hyperdash = false) =>
|
private Drawable createDrawableDroplet(bool hyperDash = false) =>
|
||||||
new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
||||||
{
|
{
|
||||||
HyperDashBindable = { Value = hyperdash }
|
HyperDashBindable = { Value = hyperDash }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
private Drawable createDrawableTinyDroplet() => new TestDrawableCatchHitObjectSpecimen(new DrawableTinyDroplet(new TinyDroplet()));
|
private Drawable createDrawableTinyDroplet() => new TestDrawableCatchHitObjectSpecimen(new DrawableTinyDroplet(new TinyDroplet()));
|
||||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
Ruleset = ruleset,
|
Ruleset = ruleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f }
|
Difficulty = new BeatmapDifficulty { CircleSize = 3.6f }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, SliderMultiplier = 2 },
|
Difficulty = new BeatmapDifficulty { CircleSize = 5, SliderMultiplier = 2 },
|
||||||
Ruleset = ruleset
|
Ruleset = ruleset
|
||||||
},
|
},
|
||||||
HitObjects = new List<HitObject>
|
HitObjects = new List<HitObject>
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||||
|
{
|
||||||
|
public class CatchPerformanceAttributes : PerformanceAttributes
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
public override PerformanceAttributes Calculate()
|
||||||
{
|
{
|
||||||
mods = Score.Mods;
|
mods = Score.Mods;
|
||||||
|
|
||||||
@ -44,15 +44,11 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
// Longer maps are worth more. "Longer" means how many hits there are which can contribute to combo
|
// Longer maps are worth more. "Longer" means how many hits there are which can contribute to combo
|
||||||
int numTotalHits = totalComboHits();
|
int numTotalHits = totalComboHits();
|
||||||
|
|
||||||
// Longer maps are worth more
|
|
||||||
double lengthBonus =
|
double lengthBonus =
|
||||||
0.95 + 0.3 * Math.Min(1.0, numTotalHits / 2500.0) +
|
0.95 + 0.3 * Math.Min(1.0, numTotalHits / 2500.0) +
|
||||||
(numTotalHits > 2500 ? Math.Log10(numTotalHits / 2500.0) * 0.475 : 0.0);
|
(numTotalHits > 2500 ? Math.Log10(numTotalHits / 2500.0) * 0.475 : 0.0);
|
||||||
|
|
||||||
// Longer maps are worth more
|
|
||||||
value *= lengthBonus;
|
value *= lengthBonus;
|
||||||
|
|
||||||
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
|
||||||
value *= Math.Pow(0.97, misses);
|
value *= Math.Pow(0.97, misses);
|
||||||
|
|
||||||
// Combo scaling
|
// Combo scaling
|
||||||
@ -80,17 +76,17 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mods.Any(m => m is ModFlashlight))
|
if (mods.Any(m => m is ModFlashlight))
|
||||||
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
|
|
||||||
value *= 1.35 * lengthBonus;
|
value *= 1.35 * lengthBonus;
|
||||||
|
|
||||||
// Scale the aim value with accuracy _slightly_
|
|
||||||
value *= Math.Pow(accuracy(), 5.5);
|
value *= Math.Pow(accuracy(), 5.5);
|
||||||
|
|
||||||
// Custom multipliers for NoFail. SpunOut is not applicable.
|
|
||||||
if (mods.Any(m => m is ModNoFail))
|
if (mods.Any(m => m is ModNoFail))
|
||||||
value *= 0.90;
|
value *= 0.90;
|
||||||
|
|
||||||
return value;
|
return new CatchPerformanceAttributes
|
||||||
|
{
|
||||||
|
Total = value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private double accuracy() => totalHits() == 0 ? 0 : Math.Clamp((double)totalSuccessfulHits() / totalHits(), 0, 1);
|
private double accuracy() => totalHits() == 0 ? 0 : Math.Clamp((double)totalSuccessfulHits() / totalHits(), 0, 1);
|
||||||
|
@ -52,16 +52,25 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HandleFlip(Direction direction)
|
public override bool HandleFlip(Direction direction, bool flipOverOrigin)
|
||||||
{
|
{
|
||||||
|
if (SelectedItems.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// This could be implemented in the future if there's a requirement for it.
|
||||||
|
if (direction == Direction.Vertical)
|
||||||
|
return false;
|
||||||
|
|
||||||
var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems);
|
var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems);
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
EditorBeatmap.PerformOnSelection(h =>
|
EditorBeatmap.PerformOnSelection(h =>
|
||||||
{
|
{
|
||||||
if (h is CatchHitObject catchObject)
|
if (h is CatchHitObject catchObject)
|
||||||
changed |= handleFlip(selectionRange, catchObject);
|
changed |= handleFlip(selectionRange, catchObject, flipOverOrigin);
|
||||||
});
|
});
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +125,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
return Math.Clamp(deltaX, lowerBound, upperBound);
|
return Math.Clamp(deltaX, lowerBound, upperBound);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool handleFlip(PositionRange selectionRange, CatchHitObject hitObject)
|
private bool handleFlip(PositionRange selectionRange, CatchHitObject hitObject, bool flipOverOrigin)
|
||||||
{
|
{
|
||||||
switch (hitObject)
|
switch (hitObject)
|
||||||
{
|
{
|
||||||
@ -124,7 +133,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
case JuiceStream juiceStream:
|
case JuiceStream juiceStream:
|
||||||
juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX);
|
juiceStream.OriginalX = getFlippedPosition(juiceStream.OriginalX);
|
||||||
|
|
||||||
foreach (var point in juiceStream.Path.ControlPoints)
|
foreach (var point in juiceStream.Path.ControlPoints)
|
||||||
point.Position *= new Vector2(-1, 1);
|
point.Position *= new Vector2(-1, 1);
|
||||||
@ -133,9 +142,11 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
hitObject.OriginalX = selectionRange.GetFlippedPosition(hitObject.OriginalX);
|
hitObject.OriginalX = getFlippedPosition(hitObject.OriginalX);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getFlippedPosition(float original) => flipOverOrigin ? CatchPlayfield.WIDTH - original : selectionRange.GetFlippedPosition(original);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,9 +90,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
|||||||
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
||||||
.FadeOut(duration * 2);
|
.FadeOut(duration * 2);
|
||||||
|
|
||||||
const float angle_variangle = 15; // should be less than 45
|
const float angle_variance = 15; // should be less than 45
|
||||||
directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4);
|
directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variance, angle_variance, randomSeed, 4);
|
||||||
directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5);
|
directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variance, angle_variance, randomSeed, 5);
|
||||||
|
|
||||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,12 @@
|
|||||||
// 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 Android.App;
|
using Android.App;
|
||||||
using Android.Content.PM;
|
|
||||||
using osu.Framework.Android;
|
using osu.Framework.Android;
|
||||||
using osu.Game.Tests;
|
using osu.Game.Tests;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.Android
|
namespace osu.Game.Rulesets.Mania.Tests.Android
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
|
[Activity(ConfigurationChanges = DEFAULT_CONFIG_CHANGES, Exported = true, LaunchMode = DEFAULT_LAUNCH_MODE, MainLauncher = true)]
|
||||||
public class MainActivity : AndroidGameActivity
|
public class MainActivity : AndroidGameActivity
|
||||||
{
|
{
|
||||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Game.Rulesets.Mania.Configuration;
|
using osu.Game.Rulesets.Mania.Configuration;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -24,9 +25,9 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetConfigCache configCache)
|
private void load()
|
||||||
{
|
{
|
||||||
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
var config = (ManiaRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull();
|
||||||
config.BindWith(ManiaRulesetSetting.ScrollDirection, direction);
|
config.BindWith(ManiaRulesetSetting.ScrollDirection, direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,13 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
private ScrollingTestContainer.TestScrollingInfo scrollingInfo = new ScrollingTestContainer.TestScrollingInfo();
|
private ScrollingTestContainer.TestScrollingInfo scrollingInfo = new ScrollingTestContainer.TestScrollingInfo();
|
||||||
|
|
||||||
[Cached(typeof(EditorBeatmap))]
|
[Cached(typeof(EditorBeatmap))]
|
||||||
private EditorBeatmap editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition()));
|
private EditorBeatmap editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition())
|
||||||
|
{
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Ruleset = new ManiaRuleset().RulesetInfo
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
private readonly ManiaBeatSnapGrid beatSnapGrid;
|
private readonly ManiaBeatSnapGrid beatSnapGrid;
|
||||||
|
|
||||||
|
@ -31,10 +31,10 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
{
|
{
|
||||||
AddStep("setup compose screen", () =>
|
AddStep("setup compose screen", () =>
|
||||||
{
|
{
|
||||||
var editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 }))
|
var editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })
|
||||||
{
|
{
|
||||||
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
|
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
|
||||||
};
|
});
|
||||||
|
|
||||||
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
||||||
|
|
||||||
|
@ -203,10 +203,10 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 }))
|
EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })
|
||||||
{
|
{
|
||||||
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }
|
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }
|
||||||
},
|
}),
|
||||||
Composer = new ManiaHitObjectComposer(new ManiaRuleset())
|
Composer = new ManiaHitObjectComposer(new ManiaRuleset())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ using osu.Game.Tests.Beatmaps;
|
|||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[Timeout(10000)]
|
|
||||||
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ManiaConvertMapping, ConvertValue>
|
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ManiaConvertMapping, ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||||
|
@ -4,11 +4,14 @@ Version: 2.5
|
|||||||
[Mania]
|
[Mania]
|
||||||
Keys: 4
|
Keys: 4
|
||||||
ColumnLineWidth: 3,1,3,1,1
|
ColumnLineWidth: 3,1,3,1,1
|
||||||
Hit0: mania/hit0
|
// some skins found in the wild had configuration keys where the @2x suffix was included in the values.
|
||||||
Hit50: mania/hit50
|
// the expected compatibility behaviour is that the presence of the @2x suffix shouldn't change anything
|
||||||
Hit100: mania/hit100
|
// if @2x assets are present.
|
||||||
Hit200: mania/hit200
|
Hit0: mania/hit0@2x
|
||||||
Hit300: mania/hit300
|
Hit50: mania/hit50@2x
|
||||||
Hit300g: mania/hit300g
|
Hit100: mania/hit100@2x
|
||||||
|
Hit200: mania/hit200@2x
|
||||||
|
Hit300: mania/hit300@2x
|
||||||
|
Hit300g: mania/hit300g@2x
|
||||||
StageLeft: mania/stage-left
|
StageLeft: mania/stage-left
|
||||||
StageRight: mania/stage-right
|
StageRight: mania/stage-right
|
@ -5,8 +5,10 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
|
using osu.Game.Rulesets.Mania.Skinning.Legacy;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -23,15 +25,24 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
if (hitWindows.IsHitResultAllowed(result))
|
if (hitWindows.IsHitResultAllowed(result))
|
||||||
{
|
{
|
||||||
AddStep("Show " + result.GetDescription(), () => SetContents(_ =>
|
AddStep("Show " + result.GetDescription(), () =>
|
||||||
new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())
|
{
|
||||||
{
|
SetContents(_ =>
|
||||||
Type = result
|
new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())
|
||||||
}, null)
|
{
|
||||||
{
|
Type = result
|
||||||
Anchor = Anchor.Centre,
|
}, null)
|
||||||
Origin = Anchor.Centre,
|
{
|
||||||
}));
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
});
|
||||||
|
|
||||||
|
// for test purposes, undo the Y adjustment related to the `ScorePosition` legacy positioning config value
|
||||||
|
// (see `LegacyManiaJudgementPiece.load()`).
|
||||||
|
// this prevents the judgements showing somewhere below or above the bounding box of the judgement.
|
||||||
|
foreach (var legacyPiece in this.ChildrenOfType<LegacyManiaJudgementPiece>())
|
||||||
|
legacyPiece.Y = 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,13 +24,13 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
|
|
||||||
public TestSceneHitExplosion()
|
public TestSceneHitExplosion()
|
||||||
{
|
{
|
||||||
int runcount = 0;
|
int runCount = 0;
|
||||||
|
|
||||||
AddRepeatStep("explode", () =>
|
AddRepeatStep("explode", () =>
|
||||||
{
|
{
|
||||||
runcount++;
|
runCount++;
|
||||||
|
|
||||||
if (runcount % 15 > 12)
|
if (runCount % 15 > 12)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int poolIndex = 0;
|
int poolIndex = 0;
|
||||||
@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
c.Add(hitExplosionPools[poolIndex].Get(e =>
|
c.Add(hitExplosionPools[poolIndex].Get(e =>
|
||||||
{
|
{
|
||||||
e.Apply(new JudgementResult(new HitObject(), runcount % 6 == 0 ? new HoldNoteTickJudgement() : new ManiaJudgement()));
|
e.Apply(new JudgementResult(new HitObject(), runCount % 6 == 0 ? new HoldNoteTickJudgement() : new ManiaJudgement()));
|
||||||
|
|
||||||
e.Anchor = Anchor.Centre;
|
e.Anchor = Anchor.Centre;
|
||||||
e.Origin = Anchor.Centre;
|
e.Origin = Anchor.Centre;
|
||||||
|
@ -264,7 +264,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
},
|
},
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
Difficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
SliderTickRate = 4,
|
SliderTickRate = 4,
|
||||||
OverallDifficulty = 10,
|
OverallDifficulty = 10,
|
||||||
@ -306,7 +306,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
},
|
},
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = tick_rate },
|
Difficulty = new BeatmapDifficulty { SliderTickRate = tick_rate },
|
||||||
Ruleset = new ManiaRuleset().RulesetInfo
|
Ruleset = new ManiaRuleset().RulesetInfo
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -383,7 +383,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
},
|
},
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 4 },
|
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 },
|
||||||
Ruleset = new ManiaRuleset().RulesetInfo
|
Ruleset = new ManiaRuleset().RulesetInfo
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -14,6 +13,7 @@ using osu.Game.Rulesets.Mania.Objects;
|
|||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Configuration;
|
using osu.Game.Rulesets.Mania.Configuration;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
@ -24,9 +24,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneTimingBasedNoteColouring : OsuTestScene
|
public class TestSceneTimingBasedNoteColouring : OsuTestScene
|
||||||
{
|
{
|
||||||
[Resolved]
|
|
||||||
private RulesetConfigCache configCache { get; set; }
|
|
||||||
|
|
||||||
private Bindable<bool> configTimingBasedNoteColouring;
|
private Bindable<bool> configTimingBasedNoteColouring;
|
||||||
|
|
||||||
private ManualClock clock;
|
private ManualClock clock;
|
||||||
@ -48,7 +45,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
});
|
});
|
||||||
AddStep("retrieve config bindable", () =>
|
AddStep("retrieve config bindable", () =>
|
||||||
{
|
{
|
||||||
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
var config = (ManiaRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull();
|
||||||
configTimingBasedNoteColouring = config.GetBindable<bool>(ManiaRulesetSetting.TimingBasedNoteColouring);
|
configTimingBasedNoteColouring = config.GetBindable<bool>(ManiaRulesetSetting.TimingBasedNoteColouring);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
public override IEnumerable<BeatmapStatistic> GetStatistics()
|
public override IEnumerable<BeatmapStatistic> GetStatistics()
|
||||||
{
|
{
|
||||||
int notes = HitObjects.Count(s => s is Note);
|
int notes = HitObjects.Count(s => s is Note);
|
||||||
int holdnotes = HitObjects.Count(s => s is HoldNote);
|
int holdNotes = HitObjects.Count(s => s is HoldNote);
|
||||||
|
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
{
|
{
|
||||||
Name = @"Hold Note Count",
|
Name = @"Hold Note Count",
|
||||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
|
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
|
||||||
Content = holdnotes.ToString(),
|
Content = holdNotes.ToString(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
public static int GetColumnCountForNonConvert(BeatmapInfo beatmapInfo)
|
public static int GetColumnCountForNonConvert(BeatmapInfo beatmapInfo)
|
||||||
{
|
{
|
||||||
double roundedCircleSize = Math.Round(beatmapInfo.BaseDifficulty.CircleSize);
|
double roundedCircleSize = Math.Round(beatmapInfo.Difficulty.CircleSize);
|
||||||
return (int)Math.Max(1, roundedCircleSize);
|
return (int)Math.Max(1, roundedCircleSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
yield return v;
|
yield return v;
|
||||||
|
|
||||||
// Todo: osu!mania doesn't output MaxCombo attribute for some reason.
|
// Todo: osu!mania doesn't output MaxCombo attribute for some reason.
|
||||||
yield return (ATTRIB_ID_STRAIN, StarRating);
|
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
|
||||||
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
|
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
|
||||||
yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier);
|
yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier);
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
{
|
{
|
||||||
base.FromDatabaseAttributes(values);
|
base.FromDatabaseAttributes(values);
|
||||||
|
|
||||||
StarRating = values[ATTRIB_ID_STRAIN];
|
StarRating = values[ATTRIB_ID_DIFFICULTY];
|
||||||
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
|
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
|
||||||
ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER];
|
ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
// 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 Newtonsoft.Json;
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Difficulty
|
||||||
|
{
|
||||||
|
public class ManiaPerformanceAttributes : PerformanceAttributes
|
||||||
|
{
|
||||||
|
[JsonProperty("difficulty")]
|
||||||
|
public double Difficulty { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("accuracy")]
|
||||||
|
public double Accuracy { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("scaled_score")]
|
||||||
|
public double ScaledScore { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
public override PerformanceAttributes Calculate()
|
||||||
{
|
{
|
||||||
mods = Score.Mods;
|
mods = Score.Mods;
|
||||||
scaledScore = Score.TotalScore;
|
scaledScore = Score.TotalScore;
|
||||||
@ -61,48 +61,46 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
if (mods.Any(m => m is ModEasy))
|
if (mods.Any(m => m is ModEasy))
|
||||||
multiplier *= 0.5;
|
multiplier *= 0.5;
|
||||||
|
|
||||||
double strainValue = computeStrainValue();
|
double difficultyValue = computeDifficultyValue();
|
||||||
double accValue = computeAccuracyValue(strainValue);
|
double accValue = computeAccuracyValue(difficultyValue);
|
||||||
double totalValue =
|
double totalValue =
|
||||||
Math.Pow(
|
Math.Pow(
|
||||||
Math.Pow(strainValue, 1.1) +
|
Math.Pow(difficultyValue, 1.1) +
|
||||||
Math.Pow(accValue, 1.1), 1.0 / 1.1
|
Math.Pow(accValue, 1.1), 1.0 / 1.1
|
||||||
) * multiplier;
|
) * multiplier;
|
||||||
|
|
||||||
if (categoryDifficulty != null)
|
return new ManiaPerformanceAttributes
|
||||||
{
|
{
|
||||||
categoryDifficulty["Strain"] = strainValue;
|
Difficulty = difficultyValue,
|
||||||
categoryDifficulty["Accuracy"] = accValue;
|
Accuracy = accValue,
|
||||||
}
|
ScaledScore = scaledScore,
|
||||||
|
Total = totalValue
|
||||||
return totalValue;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeStrainValue()
|
private double computeDifficultyValue()
|
||||||
{
|
{
|
||||||
// Obtain strain difficulty
|
double difficultyValue = Math.Pow(5 * Math.Max(1, Attributes.StarRating / 0.2) - 4.0, 2.2) / 135.0;
|
||||||
double strainValue = Math.Pow(5 * Math.Max(1, Attributes.StarRating / 0.2) - 4.0, 2.2) / 135.0;
|
|
||||||
|
|
||||||
// Longer maps are worth more
|
difficultyValue *= 1.0 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
|
||||||
strainValue *= 1.0 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
|
|
||||||
|
|
||||||
if (scaledScore <= 500000)
|
if (scaledScore <= 500000)
|
||||||
strainValue = 0;
|
difficultyValue = 0;
|
||||||
else if (scaledScore <= 600000)
|
else if (scaledScore <= 600000)
|
||||||
strainValue *= (scaledScore - 500000) / 100000 * 0.3;
|
difficultyValue *= (scaledScore - 500000) / 100000 * 0.3;
|
||||||
else if (scaledScore <= 700000)
|
else if (scaledScore <= 700000)
|
||||||
strainValue *= 0.3 + (scaledScore - 600000) / 100000 * 0.25;
|
difficultyValue *= 0.3 + (scaledScore - 600000) / 100000 * 0.25;
|
||||||
else if (scaledScore <= 800000)
|
else if (scaledScore <= 800000)
|
||||||
strainValue *= 0.55 + (scaledScore - 700000) / 100000 * 0.20;
|
difficultyValue *= 0.55 + (scaledScore - 700000) / 100000 * 0.20;
|
||||||
else if (scaledScore <= 900000)
|
else if (scaledScore <= 900000)
|
||||||
strainValue *= 0.75 + (scaledScore - 800000) / 100000 * 0.15;
|
difficultyValue *= 0.75 + (scaledScore - 800000) / 100000 * 0.15;
|
||||||
else
|
else
|
||||||
strainValue *= 0.90 + (scaledScore - 900000) / 100000 * 0.1;
|
difficultyValue *= 0.90 + (scaledScore - 900000) / 100000 * 0.1;
|
||||||
|
|
||||||
return strainValue;
|
return difficultyValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeAccuracyValue(double strainValue)
|
private double computeAccuracyValue(double difficultyValue)
|
||||||
{
|
{
|
||||||
if (Attributes.GreatHitWindow <= 0)
|
if (Attributes.GreatHitWindow <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -110,12 +108,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
// Lots of arbitrary values from testing.
|
// Lots of arbitrary values from testing.
|
||||||
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
||||||
double accuracyValue = Math.Max(0.0, 0.2 - (Attributes.GreatHitWindow - 34) * 0.006667)
|
double accuracyValue = Math.Max(0.0, 0.2 - (Attributes.GreatHitWindow - 34) * 0.006667)
|
||||||
* strainValue
|
* difficultyValue
|
||||||
* Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
|
* Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
|
||||||
|
|
||||||
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
|
||||||
// accuracyValue *= Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
|
||||||
|
|
||||||
return accuracyValue;
|
return accuracyValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||||
@ -28,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IScrollingInfo scrollingInfo)
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor();
|
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor();
|
||||||
|
|
||||||
public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new DrainingHealthProcessor(drainStartTime, 0.5);
|
public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new ManiaHealthProcessor(drainStartTime, 0.5);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this);
|
||||||
|
|
||||||
|
23
osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs
Normal file
23
osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs
Normal 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 osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Scoring
|
||||||
|
{
|
||||||
|
public class ManiaHealthProcessor : DrainingHealthProcessor
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ManiaHealthProcessor(double drainStartTime, double drainLenience = 0)
|
||||||
|
: base(drainStartTime, drainLenience)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override HitResult GetSimulatedHitResult(Judgement judgement)
|
||||||
|
{
|
||||||
|
// Users are not expected to attain perfect judgements for all notes due to the tighter hit window.
|
||||||
|
return judgement.MaxResult == HitResult.Perfect ? HitResult.Great : judgement.MaxResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
/// Mapping of <see cref="HitResult"/> to their corresponding
|
/// Mapping of <see cref="HitResult"/> to their corresponding
|
||||||
/// <see cref="LegacyManiaSkinConfigurationLookups"/> value.
|
/// <see cref="LegacyManiaSkinConfigurationLookups"/> value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly IReadOnlyDictionary<HitResult, LegacyManiaSkinConfigurationLookups> hitresult_mapping
|
private static readonly IReadOnlyDictionary<HitResult, LegacyManiaSkinConfigurationLookups> hit_result_mapping
|
||||||
= new Dictionary<HitResult, LegacyManiaSkinConfigurationLookups>
|
= new Dictionary<HitResult, LegacyManiaSkinConfigurationLookups>
|
||||||
{
|
{
|
||||||
{ HitResult.Perfect, LegacyManiaSkinConfigurationLookups.Hit300g },
|
{ HitResult.Perfect, LegacyManiaSkinConfigurationLookups.Hit300g },
|
||||||
@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
/// Mapping of <see cref="HitResult"/> to their corresponding
|
/// Mapping of <see cref="HitResult"/> to their corresponding
|
||||||
/// default filenames.
|
/// default filenames.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly IReadOnlyDictionary<HitResult, string> default_hitresult_skin_filenames
|
private static readonly IReadOnlyDictionary<HitResult, string> default_hit_result_skin_filenames
|
||||||
= new Dictionary<HitResult, string>
|
= new Dictionary<HitResult, string>
|
||||||
{
|
{
|
||||||
{ HitResult.Perfect, "mania-hit300g" },
|
{ HitResult.Perfect, "mania-hit300g" },
|
||||||
@ -126,11 +126,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
|
|
||||||
private Drawable getResult(HitResult result)
|
private Drawable getResult(HitResult result)
|
||||||
{
|
{
|
||||||
if (!hitresult_mapping.ContainsKey(result))
|
if (!hit_result_mapping.ContainsKey(result))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
string filename = this.GetManiaSkinConfig<string>(hitresult_mapping[result])?.Value
|
string filename = this.GetManiaSkinConfig<string>(hit_result_mapping[result])?.Value
|
||||||
?? default_hitresult_skin_filenames[result];
|
?? default_hit_result_skin_filenames[result];
|
||||||
|
|
||||||
var animation = this.GetAnimation(filename, true, true);
|
var animation = this.GetAnimation(filename, true, true);
|
||||||
return animation == null ? null : new LegacyManiaJudgementPiece(result, animation);
|
return animation == null ? null : new LegacyManiaJudgementPiece(result, animation);
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IScrollingInfo scrollingInfo)
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
{
|
{
|
||||||
const float angle_variangle = 15; // should be less than 45
|
const float angle_variance = 15; // should be less than 45
|
||||||
const float roundness = 80;
|
const float roundness = 80;
|
||||||
const float initial_height = 10;
|
const float initial_height = 10;
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Masking = true,
|
Masking = true,
|
||||||
Size = new Vector2(0.01f, initial_height),
|
Size = new Vector2(0.01f, initial_height),
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
|
Rotation = RNG.NextSingle(-angle_variance, angle_variance),
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Masking = true,
|
Masking = true,
|
||||||
Size = new Vector2(0.01f, initial_height),
|
Size = new Vector2(0.01f, initial_height),
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
|
Rotation = RNG.NextSingle(-angle_variance, angle_variance),
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
|
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public override bool Remove(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Remove(h);
|
public override bool Remove(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Remove(h);
|
||||||
|
|
||||||
public void Add(BarLine barline) => stages.ForEach(s => s.Add(barline));
|
public void Add(BarLine barLine) => stages.ForEach(s => s.Add(barLine));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves a column from a screen-space position.
|
/// Retrieves a column from a screen-space position.
|
||||||
|
@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public override bool Remove(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Remove(h);
|
public override bool Remove(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Remove(h);
|
||||||
|
|
||||||
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
|
public void Add(BarLine barLine) => base.Add(new DrawableBarLine(barLine));
|
||||||
|
|
||||||
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||||
{
|
{
|
||||||
|
@ -2,13 +2,12 @@
|
|||||||
// 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 Android.App;
|
using Android.App;
|
||||||
using Android.Content.PM;
|
|
||||||
using osu.Framework.Android;
|
using osu.Framework.Android;
|
||||||
using osu.Game.Tests;
|
using osu.Game.Tests;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests.Android
|
namespace osu.Game.Rulesets.Osu.Tests.Android
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
|
[Activity(ConfigurationChanges = DEFAULT_CONFIG_CHANGES, Exported = true, LaunchMode = DEFAULT_LAUNCH_MODE, MainLauncher = true)]
|
||||||
public class MainActivity : AndroidGameActivity
|
public class MainActivity : AndroidGameActivity
|
||||||
{
|
{
|
||||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||||
|
@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
|||||||
var beatmap = new Beatmap<HitObject>
|
var beatmap = new Beatmap<HitObject>
|
||||||
{
|
{
|
||||||
HitObjects = hitObjects,
|
HitObjects = hitObjects,
|
||||||
BeatmapInfo = new BeatmapInfo { BaseDifficulty = new BeatmapDifficulty(beatmapDifficulty) }
|
BeatmapInfo = new BeatmapInfo { Difficulty = new BeatmapDifficulty(beatmapDifficulty) }
|
||||||
};
|
};
|
||||||
|
|
||||||
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||||
|
@ -40,7 +40,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
public TestSceneOsuDistanceSnapGrid()
|
public TestSceneOsuDistanceSnapGrid()
|
||||||
{
|
{
|
||||||
editorBeatmap = new EditorBeatmap(new OsuBeatmap());
|
editorBeatmap = new EditorBeatmap(new OsuBeatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Ruleset = new OsuRuleset().RulesetInfo
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
// 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.Linq;
|
||||||
|
using Humanizer;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -47,6 +50,126 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddBlueprint(new TestSliderBlueprint(slider), drawableObject);
|
AddBlueprint(new TestSliderBlueprint(slider), drawableObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelection()
|
||||||
|
{
|
||||||
|
moveMouseToControlPoint(0);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
assertSelectionCount(1);
|
||||||
|
assertSelected(0);
|
||||||
|
|
||||||
|
AddStep("click right mouse", () => InputManager.Click(MouseButton.Right));
|
||||||
|
assertSelectionCount(1);
|
||||||
|
assertSelected(0);
|
||||||
|
|
||||||
|
moveMouseToControlPoint(3);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
assertSelectionCount(1);
|
||||||
|
assertSelected(3);
|
||||||
|
|
||||||
|
AddStep("press control", () => InputManager.PressKey(Key.ControlLeft));
|
||||||
|
moveMouseToControlPoint(2);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
assertSelectionCount(2);
|
||||||
|
assertSelected(2);
|
||||||
|
assertSelected(3);
|
||||||
|
|
||||||
|
moveMouseToControlPoint(0);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
assertSelectionCount(3);
|
||||||
|
assertSelected(0);
|
||||||
|
assertSelected(2);
|
||||||
|
assertSelected(3);
|
||||||
|
|
||||||
|
AddStep("click right mouse", () => InputManager.Click(MouseButton.Right));
|
||||||
|
assertSelectionCount(3);
|
||||||
|
assertSelected(0);
|
||||||
|
assertSelected(2);
|
||||||
|
assertSelected(3);
|
||||||
|
|
||||||
|
AddStep("release control", () => InputManager.ReleaseKey(Key.ControlLeft));
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
assertSelectionCount(1);
|
||||||
|
assertSelected(0);
|
||||||
|
|
||||||
|
moveMouseToRelativePosition(new Vector2(350, 0));
|
||||||
|
AddStep("ctrl+click to create new point", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
assertSelectionCount(1);
|
||||||
|
assertSelected(3);
|
||||||
|
|
||||||
|
AddStep("release ctrl+click", () =>
|
||||||
|
{
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
assertSelectionCount(1);
|
||||||
|
assertSelected(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNewControlPointCreation()
|
||||||
|
{
|
||||||
|
moveMouseToRelativePosition(new Vector2(350, 0));
|
||||||
|
AddStep("ctrl+click to create new point", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("slider has 6 control points", () => slider.Path.ControlPoints.Count == 6);
|
||||||
|
AddStep("release ctrl+click", () =>
|
||||||
|
{
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ensure that the next drag doesn't attempt to move the placement that just finished.
|
||||||
|
moveMouseToRelativePosition(new Vector2(0, 50));
|
||||||
|
AddStep("press left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
moveMouseToRelativePosition(new Vector2(0, 100));
|
||||||
|
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
assertControlPointPosition(3, new Vector2(350, 0));
|
||||||
|
|
||||||
|
moveMouseToRelativePosition(new Vector2(400, 75));
|
||||||
|
AddStep("ctrl+click to create new point", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("slider has 7 control points", () => slider.Path.ControlPoints.Count == 7);
|
||||||
|
moveMouseToRelativePosition(new Vector2(350, 75));
|
||||||
|
AddStep("release ctrl+click", () =>
|
||||||
|
{
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
assertControlPointPosition(5, new Vector2(350, 75));
|
||||||
|
|
||||||
|
// ensure that the next drag doesn't attempt to move the placement that just finished.
|
||||||
|
moveMouseToRelativePosition(new Vector2(0, 50));
|
||||||
|
AddStep("press left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
moveMouseToRelativePosition(new Vector2(0, 100));
|
||||||
|
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
assertControlPointPosition(5, new Vector2(350, 75));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertSelectionCount(int count) =>
|
||||||
|
AddAssert($"{count} control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == count);
|
||||||
|
|
||||||
|
private void assertSelected(int index) =>
|
||||||
|
AddAssert($"{(index + 1).ToOrdinalWords()} control point piece selected",
|
||||||
|
() => this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[index]).IsSelected.Value);
|
||||||
|
|
||||||
|
private void moveMouseToRelativePosition(Vector2 relativePosition) =>
|
||||||
|
AddStep($"move mouse to {relativePosition}", () =>
|
||||||
|
{
|
||||||
|
Vector2 position = slider.Position + relativePosition;
|
||||||
|
InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position));
|
||||||
|
});
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDragControlPoint()
|
public void TestDragControlPoint()
|
||||||
{
|
{
|
||||||
@ -60,6 +183,83 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertControlPointType(0, PathType.PerfectCurve);
|
assertControlPointType(0, PathType.PerfectCurve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragMultipleControlPoints()
|
||||||
|
{
|
||||||
|
moveMouseToControlPoint(2);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("hold control", () => InputManager.PressKey(Key.LControl));
|
||||||
|
|
||||||
|
moveMouseToControlPoint(3);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
moveMouseToControlPoint(4);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
moveMouseToControlPoint(2);
|
||||||
|
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
||||||
|
|
||||||
|
addMovementStep(new Vector2(450, 50));
|
||||||
|
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
||||||
|
|
||||||
|
assertControlPointPosition(2, new Vector2(450, 50));
|
||||||
|
assertControlPointType(2, PathType.PerfectCurve);
|
||||||
|
|
||||||
|
assertControlPointPosition(3, new Vector2(550, 50));
|
||||||
|
|
||||||
|
assertControlPointPosition(4, new Vector2(550, 200));
|
||||||
|
|
||||||
|
AddStep("release control", () => InputManager.ReleaseKey(Key.LControl));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragMultipleControlPointsIncludingHead()
|
||||||
|
{
|
||||||
|
moveMouseToControlPoint(0);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("hold control", () => InputManager.PressKey(Key.LControl));
|
||||||
|
|
||||||
|
moveMouseToControlPoint(3);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
moveMouseToControlPoint(4);
|
||||||
|
AddStep("click left mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
moveMouseToControlPoint(3);
|
||||||
|
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
||||||
|
|
||||||
|
addMovementStep(new Vector2(550, 50));
|
||||||
|
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
||||||
|
|
||||||
|
// note: if the head is part of the selection being moved, the entire slider is moved.
|
||||||
|
// the unselected nodes will therefore change position relative to the slider head.
|
||||||
|
|
||||||
|
AddAssert("slider moved", () => Precision.AlmostEquals(slider.Position, new Vector2(256, 192) + new Vector2(150, 50)));
|
||||||
|
|
||||||
|
assertControlPointPosition(0, Vector2.Zero);
|
||||||
|
assertControlPointType(0, PathType.PerfectCurve);
|
||||||
|
|
||||||
|
assertControlPointPosition(1, new Vector2(0, 100));
|
||||||
|
|
||||||
|
assertControlPointPosition(2, new Vector2(150, -50));
|
||||||
|
|
||||||
|
assertControlPointPosition(3, new Vector2(400, 0));
|
||||||
|
|
||||||
|
assertControlPointPosition(4, new Vector2(400, 150));
|
||||||
|
|
||||||
|
AddStep("release control", () => InputManager.ReleaseKey(Key.LControl));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDragControlPointAlmostLinearlyExterior()
|
public void TestDragControlPointAlmostLinearlyExterior()
|
||||||
{
|
{
|
||||||
|
225
osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs
Normal file
225
osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Edit;
|
||||||
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
|
{
|
||||||
|
public class TestSceneSliderSnapping : EditorTestScene
|
||||||
|
{
|
||||||
|
private const double beat_length = 1000;
|
||||||
|
|
||||||
|
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
var controlPointInfo = new ControlPointInfo();
|
||||||
|
controlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
|
||||||
|
return new TestBeatmap(ruleset, false)
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPointInfo
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Slider slider;
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
base.SetUpSteps();
|
||||||
|
|
||||||
|
AddStep("add unsnapped slider", () => EditorBeatmap.Add(slider = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Position = OsuPlayfield.BASE_SIZE / 5,
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint(Vector2.Zero),
|
||||||
|
new PathControlPoint(OsuPlayfield.BASE_SIZE * 2 / 5),
|
||||||
|
new PathControlPoint(OsuPlayfield.BASE_SIZE * 3 / 5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
AddStep("set beat divisor to 1/1", () =>
|
||||||
|
{
|
||||||
|
var beatDivisor = (BindableBeatDivisor)Editor.Dependencies.Get(typeof(BindableBeatDivisor));
|
||||||
|
beatDivisor.Value = 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMovingUnsnappedSliderNodesSnaps()
|
||||||
|
{
|
||||||
|
PathControlPointPiece sliderEnd = null;
|
||||||
|
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
AddStep("select slider end", () =>
|
||||||
|
{
|
||||||
|
sliderEnd = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints.Last());
|
||||||
|
InputManager.MoveMouseTo(sliderEnd.ScreenSpaceDrawQuad.Centre);
|
||||||
|
});
|
||||||
|
AddStep("move slider end", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
InputManager.MoveMouseTo(sliderEnd.ScreenSpaceDrawQuad.Centre - new Vector2(0, 20));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
assertSliderSnapped(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddingControlPointToUnsnappedSliderNodesSnaps()
|
||||||
|
{
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
AddStep("move mouse to new point location", () =>
|
||||||
|
{
|
||||||
|
var firstPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[0]);
|
||||||
|
var secondPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[1]);
|
||||||
|
InputManager.MoveMouseTo((firstPiece.ScreenSpaceDrawQuad.Centre + secondPiece.ScreenSpaceDrawQuad.Centre) / 2);
|
||||||
|
});
|
||||||
|
AddStep("move slider end", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
assertSliderSnapped(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRemovingControlPointFromUnsnappedSliderNodesSnaps()
|
||||||
|
{
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
AddStep("move mouse to second control point", () =>
|
||||||
|
{
|
||||||
|
var secondPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[1]);
|
||||||
|
InputManager.MoveMouseTo(secondPiece);
|
||||||
|
});
|
||||||
|
AddStep("quick delete", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ShiftLeft);
|
||||||
|
InputManager.PressButton(MouseButton.Right);
|
||||||
|
InputManager.ReleaseKey(Key.ShiftLeft);
|
||||||
|
});
|
||||||
|
assertSliderSnapped(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResizingUnsnappedSliderSnaps()
|
||||||
|
{
|
||||||
|
SelectionBoxScaleHandle handle = null;
|
||||||
|
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
AddStep("move mouse to scale handle", () =>
|
||||||
|
{
|
||||||
|
handle = this.ChildrenOfType<SelectionBoxScaleHandle>().First();
|
||||||
|
InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre);
|
||||||
|
});
|
||||||
|
AddStep("scale slider", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre + new Vector2(20, 20));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
assertSliderSnapped(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRotatingUnsnappedSliderDoesNotSnap()
|
||||||
|
{
|
||||||
|
SelectionBoxRotationHandle handle = null;
|
||||||
|
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
AddStep("move mouse to rotate handle", () =>
|
||||||
|
{
|
||||||
|
handle = this.ChildrenOfType<SelectionBoxRotationHandle>().First();
|
||||||
|
InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre);
|
||||||
|
});
|
||||||
|
AddStep("scale slider", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre + new Vector2(0, 20));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFlippingSliderDoesNotSnap()
|
||||||
|
{
|
||||||
|
OsuSelectionHandler selectionHandler = null;
|
||||||
|
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
AddStep("flip slider horizontally", () =>
|
||||||
|
{
|
||||||
|
selectionHandler = this.ChildrenOfType<OsuSelectionHandler>().Single();
|
||||||
|
selectionHandler.OnPressed(new KeyBindingPressEvent<GlobalAction>(InputManager.CurrentState, GlobalAction.EditorFlipHorizontally));
|
||||||
|
});
|
||||||
|
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
|
||||||
|
AddStep("flip slider vertically", () =>
|
||||||
|
{
|
||||||
|
selectionHandler = this.ChildrenOfType<OsuSelectionHandler>().Single();
|
||||||
|
selectionHandler.OnPressed(new KeyBindingPressEvent<GlobalAction>(InputManager.CurrentState, GlobalAction.EditorFlipVertically));
|
||||||
|
});
|
||||||
|
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestReversingSliderDoesNotSnap()
|
||||||
|
{
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
AddStep("reverse slider", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Key(Key.G);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertSliderSnapped(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertSliderSnapped(bool snapped)
|
||||||
|
=> AddAssert($"slider is {(snapped ? "" : "not ")}snapped", () =>
|
||||||
|
{
|
||||||
|
double durationInBeatLengths = slider.Duration / beat_length;
|
||||||
|
double fractionalPart = durationInBeatLengths - (int)durationInBeatLengths;
|
||||||
|
return Precision.AlmostEquals(fractionalPart, 0) == snapped;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
Difficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 8
|
CircleSize = 8
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ using osu.Game.Tests.Beatmaps;
|
|||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[Timeout(10000)]
|
|
||||||
public class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
public class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay-1@2x.png
Executable file
BIN
osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay-1@2x.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
|
Difficulty = new BeatmapDifficulty { CircleSize = 6 },
|
||||||
Ruleset = ruleset
|
Ruleset = ruleset
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 10 },
|
Difficulty = new BeatmapDifficulty { OverallDifficulty = 10 },
|
||||||
Ruleset = ruleset
|
Ruleset = ruleset
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -358,7 +358,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
},
|
},
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 },
|
Difficulty = new BeatmapDifficulty { SliderTickRate = 3 },
|
||||||
Ruleset = new OsuRuleset().RulesetInfo
|
Ruleset = new OsuRuleset().RulesetInfo
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -9,6 +9,7 @@ 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;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
@ -46,9 +47,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
=> new ClockBackedTestWorkingBeatmap(this.beatmap = beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
=> new ClockBackedTestWorkingBeatmap(this.beatmap = beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetConfigCache configCache)
|
private void load()
|
||||||
{
|
{
|
||||||
var config = (OsuRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
var config = (OsuRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull();
|
||||||
config.BindWith(OsuRulesetSetting.SnakingInSliders, snakingIn);
|
config.BindWith(OsuRulesetSetting.SnakingInSliders, snakingIn);
|
||||||
config.BindWith(OsuRulesetSetting.SnakingOutSliders, snakingOut);
|
config.BindWith(OsuRulesetSetting.SnakingOutSliders, snakingOut);
|
||||||
}
|
}
|
||||||
|
@ -364,7 +364,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
HitObjects = hitObjects,
|
HitObjects = hitObjects,
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 },
|
Difficulty = new BeatmapDifficulty { SliderTickRate = 3 },
|
||||||
Ruleset = new OsuRuleset().RulesetInfo
|
Ruleset = new OsuRuleset().RulesetInfo
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -12,14 +12,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
{
|
{
|
||||||
public class OsuDifficultyAttributes : DifficultyAttributes
|
public class OsuDifficultyAttributes : DifficultyAttributes
|
||||||
{
|
{
|
||||||
[JsonProperty("aim_strain")]
|
[JsonProperty("aim_difficulty")]
|
||||||
public double AimStrain { get; set; }
|
public double AimDifficulty { get; set; }
|
||||||
|
|
||||||
[JsonProperty("speed_strain")]
|
[JsonProperty("speed_difficulty")]
|
||||||
public double SpeedStrain { get; set; }
|
public double SpeedDifficulty { get; set; }
|
||||||
|
|
||||||
[JsonProperty("flashlight_rating")]
|
[JsonProperty("flashlight_difficulty")]
|
||||||
public double FlashlightRating { get; set; }
|
public double FlashlightDifficulty { get; set; }
|
||||||
|
|
||||||
[JsonProperty("slider_factor")]
|
[JsonProperty("slider_factor")]
|
||||||
public double SliderFactor { get; set; }
|
public double SliderFactor { get; set; }
|
||||||
@ -43,15 +43,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
foreach (var v in base.ToDatabaseAttributes())
|
foreach (var v in base.ToDatabaseAttributes())
|
||||||
yield return v;
|
yield return v;
|
||||||
|
|
||||||
yield return (ATTRIB_ID_AIM, AimStrain);
|
yield return (ATTRIB_ID_AIM, AimDifficulty);
|
||||||
yield return (ATTRIB_ID_SPEED, SpeedStrain);
|
yield return (ATTRIB_ID_SPEED, SpeedDifficulty);
|
||||||
yield return (ATTRIB_ID_OVERALL_DIFFICULTY, OverallDifficulty);
|
yield return (ATTRIB_ID_OVERALL_DIFFICULTY, OverallDifficulty);
|
||||||
yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate);
|
yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate);
|
||||||
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
|
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
|
||||||
yield return (ATTRIB_ID_STRAIN, StarRating);
|
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
|
||||||
|
|
||||||
if (ShouldSerializeFlashlightRating())
|
if (ShouldSerializeFlashlightRating())
|
||||||
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightRating);
|
yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty);
|
||||||
|
|
||||||
yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);
|
yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);
|
||||||
}
|
}
|
||||||
@ -60,18 +60,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
{
|
{
|
||||||
base.FromDatabaseAttributes(values);
|
base.FromDatabaseAttributes(values);
|
||||||
|
|
||||||
AimStrain = values[ATTRIB_ID_AIM];
|
AimDifficulty = values[ATTRIB_ID_AIM];
|
||||||
SpeedStrain = values[ATTRIB_ID_SPEED];
|
SpeedDifficulty = values[ATTRIB_ID_SPEED];
|
||||||
OverallDifficulty = values[ATTRIB_ID_OVERALL_DIFFICULTY];
|
OverallDifficulty = values[ATTRIB_ID_OVERALL_DIFFICULTY];
|
||||||
ApproachRate = values[ATTRIB_ID_APPROACH_RATE];
|
ApproachRate = values[ATTRIB_ID_APPROACH_RATE];
|
||||||
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
|
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
|
||||||
StarRating = values[ATTRIB_ID_STRAIN];
|
StarRating = values[ATTRIB_ID_DIFFICULTY];
|
||||||
FlashlightRating = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT);
|
FlashlightDifficulty = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT);
|
||||||
SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR];
|
SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used implicitly by Newtonsoft.Json to not serialize flashlight property in some cases.
|
#region Newtonsoft.Json implicit ShouldSerialize() methods
|
||||||
|
|
||||||
|
// The properties in this region are used implicitly by Newtonsoft.Json to not serialise certain fields in some cases.
|
||||||
|
// They rely on being named exactly the same as the corresponding fields (casing included) and as such should NOT be renamed
|
||||||
|
// unless the fields are also renamed.
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public bool ShouldSerializeFlashlightRating() => Mods.Any(m => m is ModFlashlight);
|
public bool ShouldSerializeFlashlightRating() => Mods.Any(m => m is ModFlashlight);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,9 +74,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
{
|
{
|
||||||
StarRating = starRating,
|
StarRating = starRating,
|
||||||
Mods = mods,
|
Mods = mods,
|
||||||
AimStrain = aimRating,
|
AimDifficulty = aimRating,
|
||||||
SpeedStrain = speedRating,
|
SpeedDifficulty = speedRating,
|
||||||
FlashlightRating = flashlightRating,
|
FlashlightDifficulty = flashlightRating,
|
||||||
SliderFactor = sliderFactor,
|
SliderFactor = sliderFactor,
|
||||||
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
|
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
|
||||||
OverallDifficulty = (80 - hitWindowGreat) / 6,
|
OverallDifficulty = (80 - hitWindowGreat) / 6,
|
||||||
|
26
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs
Normal file
26
osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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 Newtonsoft.Json;
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Difficulty
|
||||||
|
{
|
||||||
|
public class OsuPerformanceAttributes : PerformanceAttributes
|
||||||
|
{
|
||||||
|
[JsonProperty("aim")]
|
||||||
|
public double Aim { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("speed")]
|
||||||
|
public double Speed { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("accuracy")]
|
||||||
|
public double Accuracy { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("flashlight")]
|
||||||
|
public double Flashlight { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("effective_miss_count")]
|
||||||
|
public double EffectiveMissCount { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -25,14 +25,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
private int countMeh;
|
private int countMeh;
|
||||||
private int countMiss;
|
private int countMiss;
|
||||||
|
|
||||||
private int effectiveMissCount;
|
private double effectiveMissCount;
|
||||||
|
|
||||||
public OsuPerformanceCalculator(Ruleset ruleset, DifficultyAttributes attributes, ScoreInfo score)
|
public OsuPerformanceCalculator(Ruleset ruleset, DifficultyAttributes attributes, ScoreInfo score)
|
||||||
: base(ruleset, attributes, score)
|
: base(ruleset, attributes, score)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryRatings = null)
|
public override PerformanceAttributes Calculate()
|
||||||
{
|
{
|
||||||
mods = Score.Mods;
|
mods = Score.Mods;
|
||||||
accuracy = Score.Accuracy;
|
accuracy = Score.Accuracy;
|
||||||
@ -45,11 +45,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things.
|
double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things.
|
||||||
|
|
||||||
// Custom multipliers for NoFail and SpunOut.
|
|
||||||
if (mods.Any(m => m is OsuModNoFail))
|
if (mods.Any(m => m is OsuModNoFail))
|
||||||
multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount);
|
multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount);
|
||||||
|
|
||||||
if (mods.Any(m => m is OsuModSpunOut))
|
if (mods.Any(m => m is OsuModSpunOut) && totalHits > 0)
|
||||||
multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85);
|
multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85);
|
||||||
|
|
||||||
if (mods.Any(h => h is OsuModRelax))
|
if (mods.Any(h => h is OsuModRelax))
|
||||||
@ -72,42 +71,35 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
Math.Pow(flashlightValue, 1.1), 1.0 / 1.1
|
Math.Pow(flashlightValue, 1.1), 1.0 / 1.1
|
||||||
) * multiplier;
|
) * multiplier;
|
||||||
|
|
||||||
if (categoryRatings != null)
|
return new OsuPerformanceAttributes
|
||||||
{
|
{
|
||||||
categoryRatings.Add("Aim", aimValue);
|
Aim = aimValue,
|
||||||
categoryRatings.Add("Speed", speedValue);
|
Speed = speedValue,
|
||||||
categoryRatings.Add("Accuracy", accuracyValue);
|
Accuracy = accuracyValue,
|
||||||
categoryRatings.Add("Flashlight", flashlightValue);
|
Flashlight = flashlightValue,
|
||||||
categoryRatings.Add("OD", Attributes.OverallDifficulty);
|
EffectiveMissCount = effectiveMissCount,
|
||||||
categoryRatings.Add("AR", Attributes.ApproachRate);
|
Total = totalValue
|
||||||
categoryRatings.Add("Max Combo", Attributes.MaxCombo);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return totalValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeAimValue()
|
private double computeAimValue()
|
||||||
{
|
{
|
||||||
double rawAim = Attributes.AimStrain;
|
double rawAim = Attributes.AimDifficulty;
|
||||||
|
|
||||||
if (mods.Any(m => m is OsuModTouchDevice))
|
if (mods.Any(m => m is OsuModTouchDevice))
|
||||||
rawAim = Math.Pow(rawAim, 0.8);
|
rawAim = Math.Pow(rawAim, 0.8);
|
||||||
|
|
||||||
double aimValue = Math.Pow(5.0 * Math.Max(1.0, rawAim / 0.0675) - 4.0, 3.0) / 100000.0;
|
double aimValue = Math.Pow(5.0 * Math.Max(1.0, rawAim / 0.0675) - 4.0, 3.0) / 100000.0;
|
||||||
|
|
||||||
// Longer maps are worth more.
|
|
||||||
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
||||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
||||||
|
|
||||||
aimValue *= lengthBonus;
|
aimValue *= lengthBonus;
|
||||||
|
|
||||||
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
||||||
if (effectiveMissCount > 0)
|
if (effectiveMissCount > 0)
|
||||||
aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), effectiveMissCount);
|
aimValue *= 0.97 * Math.Pow(1 - Math.Pow(effectiveMissCount / totalHits, 0.775), effectiveMissCount);
|
||||||
|
|
||||||
// Combo scaling.
|
aimValue *= getComboScalingFactor();
|
||||||
if (Attributes.MaxCombo > 0)
|
|
||||||
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
|
|
||||||
|
|
||||||
double approachRateFactor = 0.0;
|
double approachRateFactor = 0.0;
|
||||||
if (Attributes.ApproachRate > 10.33)
|
if (Attributes.ApproachRate > 10.33)
|
||||||
@ -136,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
}
|
}
|
||||||
|
|
||||||
aimValue *= accuracy;
|
aimValue *= accuracy;
|
||||||
// It is important to also consider accuracy difficulty when doing that.
|
// It is important to consider accuracy difficulty when scaling with accuracy.
|
||||||
aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
|
aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
|
||||||
|
|
||||||
return aimValue;
|
return aimValue;
|
||||||
@ -144,20 +136,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
private double computeSpeedValue()
|
private double computeSpeedValue()
|
||||||
{
|
{
|
||||||
double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0;
|
double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedDifficulty / 0.0675) - 4.0, 3.0) / 100000.0;
|
||||||
|
|
||||||
// Longer maps are worth more.
|
|
||||||
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
||||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
||||||
speedValue *= lengthBonus;
|
speedValue *= lengthBonus;
|
||||||
|
|
||||||
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
||||||
if (effectiveMissCount > 0)
|
if (effectiveMissCount > 0)
|
||||||
speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));
|
speedValue *= 0.97 * Math.Pow(1 - Math.Pow(effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));
|
||||||
|
|
||||||
// Combo scaling.
|
speedValue *= getComboScalingFactor();
|
||||||
if (Attributes.MaxCombo > 0)
|
|
||||||
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
|
|
||||||
|
|
||||||
double approachRateFactor = 0.0;
|
double approachRateFactor = 0.0;
|
||||||
if (Attributes.ApproachRate > 10.33)
|
if (Attributes.ApproachRate > 10.33)
|
||||||
@ -227,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
if (!mods.Any(h => h is OsuModFlashlight))
|
if (!mods.Any(h => h is OsuModFlashlight))
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
||||||
double rawFlashlight = Attributes.FlashlightRating;
|
double rawFlashlight = Attributes.FlashlightDifficulty;
|
||||||
|
|
||||||
if (mods.Any(m => m is OsuModTouchDevice))
|
if (mods.Any(m => m is OsuModTouchDevice))
|
||||||
rawFlashlight = Math.Pow(rawFlashlight, 0.8);
|
rawFlashlight = Math.Pow(rawFlashlight, 0.8);
|
||||||
@ -236,11 +225,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
||||||
if (effectiveMissCount > 0)
|
if (effectiveMissCount > 0)
|
||||||
flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));
|
flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow(effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875));
|
||||||
|
|
||||||
// Combo scaling.
|
flashlightValue *= getComboScalingFactor();
|
||||||
if (Attributes.MaxCombo > 0)
|
|
||||||
flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
|
|
||||||
|
|
||||||
// Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius.
|
// Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius.
|
||||||
flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) +
|
flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) +
|
||||||
@ -254,7 +241,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
return flashlightValue;
|
return flashlightValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculateEffectiveMissCount()
|
private double calculateEffectiveMissCount()
|
||||||
{
|
{
|
||||||
// Guess the number of misses + slider breaks from combo
|
// Guess the number of misses + slider breaks from combo
|
||||||
double comboBasedMissCount = 0.0;
|
double comboBasedMissCount = 0.0;
|
||||||
@ -266,12 +253,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
comboBasedMissCount = fullComboThreshold / Math.Max(1.0, scoreMaxCombo);
|
comboBasedMissCount = fullComboThreshold / Math.Max(1.0, scoreMaxCombo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp misscount since it's derived from combo and can be higher than total hits and that breaks some calculations
|
// Clamp miss count since it's derived from combo and can be higher than total hits and that breaks some calculations
|
||||||
comboBasedMissCount = Math.Min(comboBasedMissCount, totalHits);
|
comboBasedMissCount = Math.Min(comboBasedMissCount, totalHits);
|
||||||
|
|
||||||
return Math.Max(countMiss, (int)Math.Floor(comboBasedMissCount));
|
return Math.Max(countMiss, comboBasedMissCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double getComboScalingFactor() => Attributes.MaxCombo <= 0 ? 1.0 : Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
|
||||||
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
||||||
private int totalSuccessfulHits => countGreat + countOk + countMeh;
|
private int totalSuccessfulHits => countGreat + countOk + countMeh;
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,9 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -33,6 +31,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
public class PathControlPointPiece : BlueprintPiece<Slider>, IHasTooltip
|
public class PathControlPointPiece : BlueprintPiece<Slider>, IHasTooltip
|
||||||
{
|
{
|
||||||
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
||||||
|
|
||||||
|
public Action<PathControlPoint> DragStarted;
|
||||||
|
public Action<DragEvent> DragInProgress;
|
||||||
|
public Action DragEnded;
|
||||||
|
|
||||||
public List<PathControlPoint> PointsInSegment;
|
public List<PathControlPoint> PointsInSegment;
|
||||||
|
|
||||||
public readonly BindableBool IsSelected = new BindableBool();
|
public readonly BindableBool IsSelected = new BindableBool();
|
||||||
@ -42,12 +45,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
private readonly Container marker;
|
private readonly Container marker;
|
||||||
private readonly Drawable markerRing;
|
private readonly Drawable markerRing;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
|
||||||
private IEditorChangeHandler changeHandler { get; set; }
|
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
|
||||||
private IPositionSnapProvider snapProvider { get; set; }
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuColour colours { get; set; }
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
@ -138,6 +135,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
updateMarkerDisplay();
|
updateMarkerDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to pair up mouse down/drag events with their corresponding mouse up events,
|
||||||
|
// to avoid deselecting the piece by accident when the mouse up corresponding to the mouse down/drag fires.
|
||||||
|
private bool keepSelection;
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
if (RequestSelection == null)
|
if (RequestSelection == null)
|
||||||
@ -146,22 +147,41 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
switch (e.Button)
|
switch (e.Button)
|
||||||
{
|
{
|
||||||
case MouseButton.Left:
|
case MouseButton.Left:
|
||||||
|
// if control is pressed, do not do anything as the user may be adding to current selection
|
||||||
|
// or dragging all currently selected control points.
|
||||||
|
// if it isn't and the user's intent is to deselect, deselection will happen on mouse up.
|
||||||
|
if (e.ControlPressed && IsSelected.Value)
|
||||||
|
return true;
|
||||||
|
|
||||||
RequestSelection.Invoke(this, e);
|
RequestSelection.Invoke(this, e);
|
||||||
|
keepSelection = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MouseButton.Right:
|
case MouseButton.Right:
|
||||||
if (!IsSelected.Value)
|
if (!IsSelected.Value)
|
||||||
RequestSelection.Invoke(this, e);
|
RequestSelection.Invoke(this, e);
|
||||||
|
|
||||||
|
keepSelection = true;
|
||||||
return false; // Allow context menu to show
|
return false; // Allow context menu to show
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e) => RequestSelection != null;
|
protected override void OnMouseUp(MouseUpEvent e)
|
||||||
|
{
|
||||||
|
base.OnMouseUp(e);
|
||||||
|
|
||||||
private Vector2 dragStartPosition;
|
// ctrl+click deselects this piece, but only if this event
|
||||||
private PathType? dragPathType;
|
// wasn't immediately preceded by a matching mouse down or drag.
|
||||||
|
if (IsSelected.Value && e.ControlPressed && !keepSelection)
|
||||||
|
IsSelected.Value = false;
|
||||||
|
|
||||||
|
keepSelection = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e) => RequestSelection != null;
|
||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e)
|
protected override bool OnDragStart(DragStartEvent e)
|
||||||
{
|
{
|
||||||
@ -170,54 +190,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
if (e.Button == MouseButton.Left)
|
if (e.Button == MouseButton.Left)
|
||||||
{
|
{
|
||||||
dragStartPosition = ControlPoint.Position;
|
DragStarted?.Invoke(ControlPoint);
|
||||||
dragPathType = PointsInSegment[0].Type;
|
keepSelection = true;
|
||||||
|
|
||||||
changeHandler?.BeginChange();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDrag(DragEvent e)
|
protected override void OnDrag(DragEvent e) => DragInProgress?.Invoke(e);
|
||||||
{
|
|
||||||
Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray();
|
|
||||||
var oldPosition = slider.Position;
|
|
||||||
double oldStartTime = slider.StartTime;
|
|
||||||
|
|
||||||
if (ControlPoint == slider.Path.ControlPoints[0])
|
protected override void OnDragEnd(DragEndEvent e) => DragEnded?.Invoke();
|
||||||
{
|
|
||||||
// Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account
|
|
||||||
var result = snapProvider?.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition);
|
|
||||||
|
|
||||||
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? e.ScreenSpaceMousePosition) - slider.Position;
|
|
||||||
|
|
||||||
slider.Position += movementDelta;
|
|
||||||
slider.StartTime = result?.Time ?? slider.StartTime;
|
|
||||||
|
|
||||||
// Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
|
|
||||||
for (int i = 1; i < slider.Path.ControlPoints.Count; i++)
|
|
||||||
slider.Path.ControlPoints[i].Position -= movementDelta;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ControlPoint.Position = dragStartPosition + (e.MousePosition - e.MouseDownPosition);
|
|
||||||
|
|
||||||
if (!slider.Path.HasValidLength)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
|
||||||
slider.Path.ControlPoints[i].Position = oldControlPoints[i];
|
|
||||||
|
|
||||||
slider.Position = oldPosition;
|
|
||||||
slider.StartTime = oldStartTime;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maintain the path type in case it got defaulted to bezier at some point during the drag.
|
|
||||||
PointsInSegment[0].Type = dragPathType;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange();
|
|
||||||
|
|
||||||
private void cachePoints(Slider slider) => PointsInSegment = slider.Path.PointsInSegment(ControlPoint);
|
private void cachePoints(Slider slider) => PointsInSegment = slider.Path.PointsInSegment(ControlPoint);
|
||||||
|
|
||||||
@ -267,7 +250,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
switch (pathType)
|
switch (pathType)
|
||||||
{
|
{
|
||||||
case PathType.Catmull:
|
case PathType.Catmull:
|
||||||
return colours.Seafoam;
|
return colours.SeaFoam;
|
||||||
|
|
||||||
case PathType.Bezier:
|
case PathType.Bezier:
|
||||||
return colours.Pink;
|
return colours.Pink;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -16,6 +17,7 @@ using osu.Framework.Input;
|
|||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -40,6 +42,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
public Action<List<PathControlPoint>> RemoveControlPointsRequested;
|
public Action<List<PathControlPoint>> RemoveControlPointsRequested;
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private IPositionSnapProvider snapProvider { get; set; }
|
||||||
|
|
||||||
public PathControlPointVisualiser(Slider slider, bool allowSelection)
|
public PathControlPointVisualiser(Slider slider, bool allowSelection)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
this.slider = slider;
|
||||||
@ -64,6 +69,39 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
controlPoints.BindTo(slider.Path.ControlPoints);
|
controlPoints.BindTo(slider.Path.ControlPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects the <see cref="PathControlPointPiece"/> corresponding to the given <paramref name="pathControlPoint"/>,
|
||||||
|
/// and deselects all other <see cref="PathControlPointPiece"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public void SetSelectionTo(PathControlPoint pathControlPoint)
|
||||||
|
{
|
||||||
|
foreach (var p in Pieces)
|
||||||
|
p.IsSelected.Value = p.ControlPoint == pathControlPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete all visually selected <see cref="PathControlPoint"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool DeleteSelected()
|
||||||
|
{
|
||||||
|
List<PathControlPoint> toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.ControlPoint).ToList();
|
||||||
|
|
||||||
|
// Ensure that there are any points to be deleted
|
||||||
|
if (toRemove.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
changeHandler?.BeginChange();
|
||||||
|
RemoveControlPointsRequested?.Invoke(toRemove);
|
||||||
|
changeHandler?.EndChange();
|
||||||
|
|
||||||
|
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
|
||||||
|
foreach (var piece in Pieces)
|
||||||
|
piece.IsSelected.Value = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void onControlPointsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
private void onControlPointsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
@ -87,7 +125,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
|
Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
|
||||||
{
|
{
|
||||||
if (allowSelection)
|
if (allowSelection)
|
||||||
d.RequestSelection = selectPiece;
|
d.RequestSelection = selectionRequested;
|
||||||
|
|
||||||
|
d.DragStarted = dragStarted;
|
||||||
|
d.DragInProgress = dragInProgress;
|
||||||
|
d.DragEnded = dragEnded;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Connections.Add(new PathControlPointConnectionPiece(slider, e.NewStartingIndex + i));
|
Connections.Add(new PathControlPointConnectionPiece(slider, e.NewStartingIndex + i));
|
||||||
@ -119,6 +161,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
|
if (Pieces.Any(piece => piece.IsHovered))
|
||||||
|
return false;
|
||||||
|
|
||||||
foreach (var piece in Pieces)
|
foreach (var piece in Pieces)
|
||||||
{
|
{
|
||||||
piece.IsSelected.Value = false;
|
piece.IsSelected.Value = false;
|
||||||
@ -142,15 +187,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
|
private void selectionRequested(PathControlPointPiece piece, MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
||||||
piece.IsSelected.Toggle();
|
piece.IsSelected.Toggle();
|
||||||
else
|
else
|
||||||
{
|
SetSelectionTo(piece.ControlPoint);
|
||||||
foreach (var p in Pieces)
|
|
||||||
p.IsSelected.Value = p == piece;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -184,25 +226,87 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IEditorChangeHandler changeHandler { get; set; }
|
private IEditorChangeHandler changeHandler { get; set; }
|
||||||
|
|
||||||
public bool DeleteSelected()
|
#region Drag handling
|
||||||
{
|
|
||||||
List<PathControlPoint> toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.ControlPoint).ToList();
|
|
||||||
|
|
||||||
// Ensure that there are any points to be deleted
|
private Vector2[] dragStartPositions;
|
||||||
if (toRemove.Count == 0)
|
private PathType?[] dragPathTypes;
|
||||||
return false;
|
private int draggedControlPointIndex;
|
||||||
|
private HashSet<PathControlPoint> selectedControlPoints;
|
||||||
|
|
||||||
|
private void dragStarted(PathControlPoint controlPoint)
|
||||||
|
{
|
||||||
|
dragStartPositions = slider.Path.ControlPoints.Select(point => point.Position).ToArray();
|
||||||
|
dragPathTypes = slider.Path.ControlPoints.Select(point => point.Type).ToArray();
|
||||||
|
draggedControlPointIndex = slider.Path.ControlPoints.IndexOf(controlPoint);
|
||||||
|
selectedControlPoints = new HashSet<PathControlPoint>(Pieces.Where(piece => piece.IsSelected.Value).Select(piece => piece.ControlPoint));
|
||||||
|
|
||||||
|
Debug.Assert(draggedControlPointIndex >= 0);
|
||||||
|
|
||||||
changeHandler?.BeginChange();
|
changeHandler?.BeginChange();
|
||||||
RemoveControlPointsRequested?.Invoke(toRemove);
|
|
||||||
changeHandler?.EndChange();
|
|
||||||
|
|
||||||
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
|
|
||||||
foreach (var piece in Pieces)
|
|
||||||
piece.IsSelected.Value = false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dragInProgress(DragEvent e)
|
||||||
|
{
|
||||||
|
Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray();
|
||||||
|
var oldPosition = slider.Position;
|
||||||
|
double oldStartTime = slider.StartTime;
|
||||||
|
|
||||||
|
if (selectedControlPoints.Contains(slider.Path.ControlPoints[0]))
|
||||||
|
{
|
||||||
|
// Special handling for selections containing head control point - the position of the slider changes which means the snapped position and time have to be taken into account
|
||||||
|
Vector2 newHeadPosition = Parent.ToScreenSpace(e.MousePosition + (dragStartPositions[0] - dragStartPositions[draggedControlPointIndex]));
|
||||||
|
var result = snapProvider?.SnapScreenSpacePositionToValidTime(newHeadPosition);
|
||||||
|
|
||||||
|
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? newHeadPosition) - slider.Position;
|
||||||
|
|
||||||
|
slider.Position += movementDelta;
|
||||||
|
slider.StartTime = result?.Time ?? slider.StartTime;
|
||||||
|
|
||||||
|
for (int i = 1; i < slider.Path.ControlPoints.Count; i++)
|
||||||
|
{
|
||||||
|
var controlPoint = slider.Path.ControlPoints[i];
|
||||||
|
// Since control points are relative to the position of the slider, all points that are _not_ selected
|
||||||
|
// need to be offset _back_ by the delta corresponding to the movement of the head point.
|
||||||
|
// All other selected control points (if any) will move together with the head point
|
||||||
|
// (and so they will not move at all, relative to each other).
|
||||||
|
if (!selectedControlPoints.Contains(controlPoint))
|
||||||
|
controlPoint.Position -= movementDelta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < controlPoints.Count; ++i)
|
||||||
|
{
|
||||||
|
var controlPoint = controlPoints[i];
|
||||||
|
if (selectedControlPoints.Contains(controlPoint))
|
||||||
|
controlPoint.Position = dragStartPositions[i] + (e.MousePosition - e.MouseDownPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snap the path to the current beat divisor before checking length validity.
|
||||||
|
slider.SnapTo(snapProvider);
|
||||||
|
|
||||||
|
if (!slider.Path.HasValidLength)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||||
|
slider.Path.ControlPoints[i].Position = oldControlPoints[i];
|
||||||
|
|
||||||
|
slider.Position = oldPosition;
|
||||||
|
slider.StartTime = oldStartTime;
|
||||||
|
// Snap the path length again to undo the invalid length.
|
||||||
|
slider.SnapTo(snapProvider);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maintain the path types in case they got defaulted to bezier at some point during the drag.
|
||||||
|
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||||
|
slider.Path.ControlPoints[i].Type = dragPathTypes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dragEnded() => changeHandler?.EndChange();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public MenuItem[] ContextMenuItems
|
public MenuItem[] ContextMenuItems
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
@ -50,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// 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.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -81,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
controlPoints.BindTo(HitObject.Path.ControlPoints);
|
controlPoints.BindTo(HitObject.Path.ControlPoints);
|
||||||
|
|
||||||
pathVersion.BindTo(HitObject.Path.Version);
|
pathVersion.BindTo(HitObject.Path.Version);
|
||||||
pathVersion.BindValueChanged(_ => updatePath());
|
pathVersion.BindValueChanged(_ => editorBeatmap?.Update(HitObject));
|
||||||
|
|
||||||
BodyPiece.UpdateFrom(HitObject);
|
BodyPiece.UpdateFrom(HitObject);
|
||||||
}
|
}
|
||||||
@ -140,7 +139,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
case MouseButton.Left:
|
case MouseButton.Left:
|
||||||
if (e.ControlPressed && IsSelected)
|
if (e.ControlPressed && IsSelected)
|
||||||
{
|
{
|
||||||
placementControlPointIndex = addControlPoint(e.MousePosition);
|
changeHandler?.BeginChange();
|
||||||
|
placementControlPoint = addControlPoint(e.MousePosition);
|
||||||
|
ControlPointVisualiser?.SetSelectionTo(placementControlPoint);
|
||||||
return true; // Stop input from being handled and modifying the selection
|
return true; // Stop input from being handled and modifying the selection
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,31 +151,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int? placementControlPointIndex;
|
[CanBeNull]
|
||||||
|
private PathControlPoint placementControlPoint;
|
||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e)
|
protected override bool OnDragStart(DragStartEvent e) => placementControlPoint != null;
|
||||||
{
|
|
||||||
if (placementControlPointIndex != null)
|
|
||||||
{
|
|
||||||
changeHandler?.BeginChange();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDrag(DragEvent e)
|
protected override void OnDrag(DragEvent e)
|
||||||
{
|
{
|
||||||
Debug.Assert(placementControlPointIndex != null);
|
if (placementControlPoint != null)
|
||||||
|
placementControlPoint.Position = e.MousePosition - HitObject.Position;
|
||||||
HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position = e.MousePosition - HitObject.Position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDragEnd(DragEndEvent e)
|
protected override void OnMouseUp(MouseUpEvent e)
|
||||||
{
|
{
|
||||||
if (placementControlPointIndex != null)
|
if (placementControlPoint != null)
|
||||||
{
|
{
|
||||||
placementControlPointIndex = null;
|
placementControlPoint = null;
|
||||||
changeHandler?.EndChange();
|
changeHandler?.EndChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,7 +185,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int addControlPoint(Vector2 position)
|
private PathControlPoint addControlPoint(Vector2 position)
|
||||||
{
|
{
|
||||||
position -= HitObject.Position;
|
position -= HitObject.Position;
|
||||||
|
|
||||||
@ -211,10 +203,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the control points from the insertion index onwards to make room for the insertion
|
var pathControlPoint = new PathControlPoint { Position = position };
|
||||||
controlPoints.Insert(insertionIndex, new PathControlPoint { Position = position });
|
|
||||||
|
|
||||||
return insertionIndex;
|
// Move the control points from the insertion index onwards to make room for the insertion
|
||||||
|
controlPoints.Insert(insertionIndex, pathControlPoint);
|
||||||
|
|
||||||
|
HitObject.SnapTo(composer);
|
||||||
|
|
||||||
|
return pathControlPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeControlPoints(List<PathControlPoint> toRemove)
|
private void removeControlPoints(List<PathControlPoint> toRemove)
|
||||||
@ -233,7 +229,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
controlPoints.Remove(c);
|
controlPoints.Remove(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted
|
// Snap the slider to the current beat divisor before checking length validity.
|
||||||
|
HitObject.SnapTo(composer);
|
||||||
|
|
||||||
|
// If there are 0 or 1 remaining control points, or the slider has an invalid length, it is in a degenerate form and should be deleted
|
||||||
if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLength)
|
if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLength)
|
||||||
{
|
{
|
||||||
placementHandler?.Delete(HitObject);
|
placementHandler?.Delete(HitObject);
|
||||||
@ -248,12 +247,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
HitObject.Position += first;
|
HitObject.Position += first;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePath()
|
|
||||||
{
|
|
||||||
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
|
|
||||||
editorBeatmap?.Update(HitObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void convertToStream()
|
private void convertToStream()
|
||||||
{
|
{
|
||||||
if (editorBeatmap == null || changeHandler == null || beatDivisor == null)
|
if (editorBeatmap == null || changeHandler == null || beatDivisor == null)
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -17,6 +22,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
public class OsuSelectionHandler : EditorSelectionHandler
|
public class OsuSelectionHandler : EditorSelectionHandler
|
||||||
{
|
{
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private IPositionSnapProvider? positionSnapProvider { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// During a transform, the initial origin is stored so it can be used throughout the operation.
|
/// During a transform, the initial origin is stored so it can be used throughout the operation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -26,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
/// During a transform, the initial path types of a single selected slider are stored so they
|
/// During a transform, the initial path types of a single selected slider are stored so they
|
||||||
/// can be maintained throughout the operation.
|
/// can be maintained throughout the operation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<PathType?> referencePathTypes;
|
private List<PathType?>? referencePathTypes;
|
||||||
|
|
||||||
protected override void OnSelectionChanged()
|
protected override void OnSelectionChanged()
|
||||||
{
|
{
|
||||||
@ -84,18 +92,28 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HandleFlip(Direction direction)
|
public override bool HandleFlip(Direction direction, bool flipOverOrigin)
|
||||||
{
|
{
|
||||||
var hitObjects = selectedMovableObjects;
|
var hitObjects = selectedMovableObjects;
|
||||||
|
|
||||||
var selectedObjectsQuad = getSurroundingQuad(hitObjects);
|
var flipQuad = flipOverOrigin ? new Quad(0, 0, OsuPlayfield.BASE_SIZE.X, OsuPlayfield.BASE_SIZE.Y) : getSurroundingQuad(hitObjects);
|
||||||
|
|
||||||
|
bool didFlip = false;
|
||||||
|
|
||||||
foreach (var h in hitObjects)
|
foreach (var h in hitObjects)
|
||||||
{
|
{
|
||||||
h.Position = GetFlippedPosition(direction, selectedObjectsQuad, h.Position);
|
var flippedPosition = GetFlippedPosition(direction, flipQuad, h.Position);
|
||||||
|
|
||||||
|
if (!Precision.AlmostEquals(flippedPosition, h.Position))
|
||||||
|
{
|
||||||
|
h.Position = flippedPosition;
|
||||||
|
didFlip = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (h is Slider slider)
|
if (h is Slider slider)
|
||||||
{
|
{
|
||||||
|
didFlip = true;
|
||||||
|
|
||||||
foreach (var point in slider.Path.ControlPoints)
|
foreach (var point in slider.Path.ControlPoints)
|
||||||
{
|
{
|
||||||
point.Position = new Vector2(
|
point.Position = new Vector2(
|
||||||
@ -106,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return didFlip;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HandleScale(Vector2 scale, Anchor reference)
|
public override bool HandleScale(Vector2 scale, Anchor reference)
|
||||||
@ -186,6 +204,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
for (int i = 0; i < slider.Path.ControlPoints.Count; ++i)
|
for (int i = 0; i < slider.Path.ControlPoints.Count; ++i)
|
||||||
slider.Path.ControlPoints[i].Type = referencePathTypes[i];
|
slider.Path.ControlPoints[i].Type = referencePathTypes[i];
|
||||||
|
|
||||||
|
// Snap the slider's length to the current beat divisor
|
||||||
|
// to calculate the final resulting duration / bounding box before the final checks.
|
||||||
|
slider.SnapTo(positionSnapProvider);
|
||||||
|
|
||||||
//if sliderhead or sliderend end up outside playfield, revert scaling.
|
//if sliderhead or sliderend end up outside playfield, revert scaling.
|
||||||
Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider });
|
Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider });
|
||||||
(bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad);
|
(bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad);
|
||||||
@ -195,6 +217,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
foreach (var point in slider.Path.ControlPoints)
|
foreach (var point in slider.Path.ControlPoints)
|
||||||
point.Position = oldControlPoints.Dequeue();
|
point.Position = oldControlPoints.Dequeue();
|
||||||
|
|
||||||
|
// Snap the slider's length again to undo the potentially-invalid length applied by the previous snap.
|
||||||
|
slider.SnapTo(positionSnapProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)
|
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)
|
||||||
|
@ -16,7 +16,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.Automation;
|
public override ModType Type => ModType.Automation;
|
||||||
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;
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load()
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
@ -65,8 +65,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
double startTime = StartTime + (float)(i + 1) / totalSpins * Duration;
|
double startTime = StartTime + (float)(i + 1) / totalSpins * Duration;
|
||||||
|
|
||||||
AddNested(i < SpinsRequired
|
AddNested(i < SpinsRequired
|
||||||
? new SpinnerTick { StartTime = startTime }
|
? new SpinnerTick { StartTime = startTime, Position = Position }
|
||||||
: new SpinnerBonusTick { StartTime = startTime });
|
: new SpinnerBonusTick { StartTime = startTime, Position = Position });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,15 +3,13 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||||
{
|
{
|
||||||
public class SpinnerBackgroundLayer : SpinnerFill
|
public class SpinnerBackgroundLayer : SpinnerFill
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, DrawableHitObject drawableHitObject)
|
private void load()
|
||||||
{
|
{
|
||||||
Disc.Alpha = 0;
|
Disc.Alpha = 0;
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
// 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 osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
|
{
|
||||||
|
internal class KiaiFlashingDrawable : BeatSyncedContainer
|
||||||
|
{
|
||||||
|
private readonly Drawable flashingDrawable;
|
||||||
|
|
||||||
|
private const float flash_opacity = 0.3f;
|
||||||
|
|
||||||
|
public KiaiFlashingDrawable(Func<Drawable?> creationFunc)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
(creationFunc.Invoke() ?? Empty()).With(d =>
|
||||||
|
{
|
||||||
|
d.Anchor = Anchor.Centre;
|
||||||
|
d.Origin = Anchor.Centre;
|
||||||
|
}),
|
||||||
|
flashingDrawable = (creationFunc.Invoke() ?? Empty()).With(d =>
|
||||||
|
{
|
||||||
|
d.Anchor = Anchor.Centre;
|
||||||
|
d.Origin = Anchor.Centre;
|
||||||
|
d.Alpha = 0;
|
||||||
|
d.Blending = BlendingParameters.Additive;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||||
|
{
|
||||||
|
if (!effectPoint.KiaiMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flashingDrawable
|
||||||
|
.FadeTo(flash_opacity)
|
||||||
|
.Then()
|
||||||
|
.FadeOut(timingPoint.BeatLength * 0.75f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,61 +0,0 @@
|
|||||||
// 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 osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Graphics.Textures;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|
||||||
{
|
|
||||||
internal class KiaiFlashingSprite : BeatSyncedContainer
|
|
||||||
{
|
|
||||||
private readonly Sprite mainSprite;
|
|
||||||
private readonly Sprite flashingSprite;
|
|
||||||
|
|
||||||
public Texture Texture
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
mainSprite.Texture = value;
|
|
||||||
flashingSprite.Texture = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const float flash_opacity = 0.3f;
|
|
||||||
|
|
||||||
public KiaiFlashingSprite()
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
mainSprite = new Sprite
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
},
|
|
||||||
flashingSprite = new Sprite
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Alpha = 0,
|
|
||||||
Blending = BlendingParameters.Additive,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
|
||||||
{
|
|
||||||
if (!effectPoint.KiaiMode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
flashingSprite
|
|
||||||
.FadeTo(flash_opacity)
|
|
||||||
.Then()
|
|
||||||
.FadeOut(timingPoint.BeatLength * 0.75f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
private GameplayState gameplayState { get; set; }
|
private GameplayState gameplayState { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin, OsuColour colours)
|
private void load(ISkinSource skin)
|
||||||
{
|
{
|
||||||
var texture = skin.GetTexture("star2");
|
var texture = skin.GetTexture("star2");
|
||||||
var starBreakAdditive = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.StarBreakAdditive)?.Value ?? new Color4(255, 182, 193, 255);
|
var starBreakAdditive = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.StarBreakAdditive)?.Value ?? new Color4(255, 182, 193, 255);
|
||||||
|
@ -5,6 +5,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -68,13 +69,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
// at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it.
|
// at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it.
|
||||||
// the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist.
|
// the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist.
|
||||||
// expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin).
|
// expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin).
|
||||||
Texture overlayTexture = getTextureWithFallback("overlay");
|
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
hitCircleSprite = new KiaiFlashingSprite
|
hitCircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = baseTexture })
|
||||||
{
|
{
|
||||||
Texture = baseTexture,
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
@ -82,9 +81,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Child = hitCircleOverlay = new KiaiFlashingSprite
|
Child = hitCircleOverlay = new KiaiFlashingDrawable(() => getAnimationWithFallback(@"overlay", 1000 / 2d))
|
||||||
{
|
{
|
||||||
Texture = overlayTexture,
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
@ -126,6 +124,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
return tex ?? skin.GetTexture($"hitcircle{name}");
|
return tex ?? skin.GetTexture($"hitcircle{name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Drawable getAnimationWithFallback(string name, double frameLength)
|
||||||
|
{
|
||||||
|
Drawable animation = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(priorityLookup))
|
||||||
|
{
|
||||||
|
animation = skin.GetAnimation($"{priorityLookup}{name}", true, true, frameLength: frameLength);
|
||||||
|
|
||||||
|
if (!allowFallback)
|
||||||
|
return animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return animation ?? skin.GetAnimation($"hitcircle{name}", true, true, frameLength: frameLength);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -12,6 +12,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
CursorExpand,
|
CursorExpand,
|
||||||
CursorRotate,
|
CursorRotate,
|
||||||
HitCircleOverlayAboveNumber,
|
HitCircleOverlayAboveNumber,
|
||||||
|
|
||||||
|
// ReSharper disable once IdentifierTypo
|
||||||
HitCircleOverlayAboveNumer, // Some old skins will have this typo
|
HitCircleOverlayAboveNumer, // Some old skins will have this typo
|
||||||
SpinnerFrequencyModulate,
|
SpinnerFrequencyModulate,
|
||||||
SpinnerNoBlink
|
SpinnerNoBlink
|
||||||
|
@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.Statistics
|
|||||||
|
|
||||||
pointGrid.Content = points;
|
pointGrid.Content = points;
|
||||||
|
|
||||||
if (score.HitEvents == null || score.HitEvents.Count == 0)
|
if (score.HitEvents.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Todo: This should probably not be done like this.
|
// Todo: This should probably not be done like this.
|
||||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
private OsuConfigManager config { get; set; }
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OsuConfigManager config, OsuRulesetConfigManager rulesetConfig)
|
private void load(OsuRulesetConfigManager rulesetConfig)
|
||||||
{
|
{
|
||||||
rulesetConfig?.BindWith(OsuRulesetSetting.ShowCursorTrail, showTrail);
|
rulesetConfig?.BindWith(OsuRulesetSetting.ShowCursorTrail, showTrail);
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,12 @@
|
|||||||
// 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 Android.App;
|
using Android.App;
|
||||||
using Android.Content.PM;
|
|
||||||
using osu.Framework.Android;
|
using osu.Framework.Android;
|
||||||
using osu.Game.Tests;
|
using osu.Game.Tests;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests.Android
|
namespace osu.Game.Rulesets.Taiko.Tests.Android
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
|
[Activity(ConfigurationChanges = DEFAULT_CONFIG_CHANGES, Exported = true, LaunchMode = DEFAULT_LAUNCH_MODE, MainLauncher = true)]
|
||||||
public class MainActivity : AndroidGameActivity
|
public class MainActivity : AndroidGameActivity
|
||||||
{
|
{
|
||||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||||
|
@ -32,12 +32,12 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
HitObjects = new List<HitObject> { new Hit { Type = HitType.Centre } },
|
HitObjects = new List<HitObject> { new Hit { Type = HitType.Centre } },
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty(),
|
Difficulty = new BeatmapDifficulty(),
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Artist = @"Unknown",
|
Artist = @"Unknown",
|
||||||
Title = @"Sample Beatmap",
|
Title = @"Sample Beatmap",
|
||||||
AuthorString = @"peppy",
|
Author = { Username = @"peppy" },
|
||||||
},
|
},
|
||||||
Ruleset = new TaikoRuleset().RulesetInfo
|
Ruleset = new TaikoRuleset().RulesetInfo
|
||||||
},
|
},
|
||||||
|
@ -40,10 +40,10 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor
|
|||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
EditorBeatmap = new EditorBeatmap(new TaikoBeatmap())
|
EditorBeatmap = new EditorBeatmap(new TaikoBeatmap
|
||||||
{
|
{
|
||||||
BeatmapInfo = { Ruleset = new TaikoRuleset().RulesetInfo }
|
BeatmapInfo = { Ruleset = new TaikoRuleset().RulesetInfo }
|
||||||
},
|
}),
|
||||||
new TaikoHitObjectComposer(new TaikoRuleset())
|
new TaikoHitObjectComposer(new TaikoRuleset())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
|
|
||||||
private BarLine createBarLineAtCurrentTime(bool major = false)
|
private BarLine createBarLineAtCurrentTime(bool major = false)
|
||||||
{
|
{
|
||||||
var barline = new BarLine
|
var barLine = new BarLine
|
||||||
{
|
{
|
||||||
Major = major,
|
Major = major,
|
||||||
StartTime = Time.Current + 2000,
|
StartTime = Time.Current + 2000,
|
||||||
@ -92,9 +92,9 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
var cpi = new ControlPointInfo();
|
var cpi = new ControlPointInfo();
|
||||||
cpi.Add(0, new TimingControlPoint { BeatLength = 500 });
|
cpi.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
|
||||||
barline.ApplyDefaults(cpi, new BeatmapDifficulty());
|
barLine.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
return barline;
|
return barLine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,12 +158,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
HitObjects = new List<HitObject> { new Hit { Type = HitType.Centre } },
|
HitObjects = new List<HitObject> { new Hit { Type = HitType.Centre } },
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty(),
|
Difficulty = new BeatmapDifficulty(),
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Artist = "Unknown",
|
Artist = "Unknown",
|
||||||
Title = "Sample Beatmap",
|
Title = "Sample Beatmap",
|
||||||
AuthorString = "Craftplacer",
|
Author = { Username = "Craftplacer" },
|
||||||
},
|
},
|
||||||
Ruleset = new TaikoRuleset().RulesetInfo
|
Ruleset = new TaikoRuleset().RulesetInfo
|
||||||
},
|
},
|
||||||
|
@ -12,7 +12,6 @@ using osu.Game.Tests.Beatmaps;
|
|||||||
namespace osu.Game.Rulesets.Taiko.Tests
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[Timeout(10000)]
|
|
||||||
public class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
public class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user