mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 07:33:20 +08:00
Merge remote-tracking branch 'upstream/master' into test-scene-create-ruleset
# Conflicts: # osu.Game.Rulesets.Catch.Tests/TestSceneBananaShower.cs # osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs # osu.Game/Tests/Visual/ModTestScene.cs # osu.Game/Tests/Visual/OsuTestScene.cs
This commit is contained in:
commit
84b40ea2c6
@ -4,3 +4,5 @@ M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(
|
|||||||
M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead.
|
M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead.
|
||||||
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
||||||
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
||||||
|
T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods.
|
||||||
|
T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods.
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
<EmbeddedResource Include="Resources\**\*.*" />
|
<EmbeddedResource Include="Resources\**\*.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Code Analysis">
|
<ItemGroup Label="Code Analysis">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="2.9.8" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.0.0" PrivateAssets="All" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Code Analysis">
|
<PropertyGroup Label="Code Analysis">
|
||||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
||||||
|
@ -15,6 +15,8 @@ Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the
|
|||||||
|
|
||||||
This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
|
This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
|
||||||
|
|
||||||
|
**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passses come at the end of development, preceeded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to the stable releases of osu! (found on the website). We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
|
||||||
|
|
||||||
We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
|
We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
|
||||||
|
|
||||||
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
|
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.412.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.602.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.421.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.601.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -18,7 +18,8 @@ namespace osu.Android
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string versionName = packageInfo.VersionCode.ToString();
|
// todo: needs checking before play store redeploy.
|
||||||
|
string versionName = packageInfo.VersionName;
|
||||||
// undo play store version garbling
|
// undo play store version garbling
|
||||||
return new Version(int.Parse(versionName.Substring(0, 4)), int.Parse(versionName.Substring(4, 4)), int.Parse(versionName.Substring(8, 1)));
|
return new Version(int.Parse(versionName.Substring(0, 4)), int.Parse(versionName.Substring(4, 4)), int.Parse(versionName.Substring(8, 1)));
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,14 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Win32;
|
||||||
using osu.Desktop.Overlays;
|
using osu.Desktop.Overlays;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
using Microsoft.Win32;
|
|
||||||
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.Platform.Windows;
|
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
@ -37,7 +36,11 @@ namespace osu.Desktop
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Host is DesktopGameHost desktopHost)
|
if (Host is DesktopGameHost desktopHost)
|
||||||
return new StableStorage(desktopHost);
|
{
|
||||||
|
string stablePath = getStableInstallPath();
|
||||||
|
if (!string.IsNullOrEmpty(stablePath))
|
||||||
|
return new DesktopStorage(stablePath, desktopHost);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@ -47,6 +50,35 @@ namespace osu.Desktop
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string getStableInstallPath()
|
||||||
|
{
|
||||||
|
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
||||||
|
|
||||||
|
string stableInstallPath;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
||||||
|
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
|
||||||
|
|
||||||
|
if (checkExists(stableInstallPath))
|
||||||
|
return stableInstallPath;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
|
||||||
|
if (checkExists(stableInstallPath))
|
||||||
|
return stableInstallPath;
|
||||||
|
|
||||||
|
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
|
||||||
|
if (checkExists(stableInstallPath))
|
||||||
|
return stableInstallPath;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected override UpdateManager CreateUpdateManager()
|
protected override UpdateManager CreateUpdateManager()
|
||||||
{
|
{
|
||||||
switch (RuntimeInfo.OS)
|
switch (RuntimeInfo.OS)
|
||||||
@ -111,45 +143,5 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A method of accessing an osu-stable install in a controlled fashion.
|
|
||||||
/// </summary>
|
|
||||||
private class StableStorage : WindowsStorage
|
|
||||||
{
|
|
||||||
protected override string LocateBasePath()
|
|
||||||
{
|
|
||||||
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
|
||||||
|
|
||||||
string stableInstallPath;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
|
||||||
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
|
|
||||||
|
|
||||||
if (checkExists(stableInstallPath))
|
|
||||||
return stableInstallPath;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
|
|
||||||
if (checkExists(stableInstallPath))
|
|
||||||
return stableInstallPath;
|
|
||||||
|
|
||||||
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
|
|
||||||
if (checkExists(stableInstallPath))
|
|
||||||
return stableInstallPath;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StableStorage(DesktopGameHost host)
|
|
||||||
: base(string.Empty, host)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,11 @@ namespace osu.Desktop
|
|||||||
if (args.Length > 0 && args[0].Contains('.')) // easy way to check for a file import in args
|
if (args.Length > 0 && args[0].Contains('.')) // easy way to check for a file import in args
|
||||||
{
|
{
|
||||||
var importer = new ArchiveImportIPCChannel(host);
|
var importer = new ArchiveImportIPCChannel(host);
|
||||||
// Restore the cwd so relative paths given at the command line work correctly
|
|
||||||
Directory.SetCurrentDirectory(cwd);
|
|
||||||
|
|
||||||
foreach (var file in args)
|
foreach (var file in args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"Importing {0}", file);
|
Console.WriteLine(@"Importing {0}", file);
|
||||||
if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000))
|
if (!importer.ImportAsync(Path.GetFullPath(file, cwd)).Wait(3000))
|
||||||
throw new TimeoutException(@"IPC took too long to send");
|
throw new TimeoutException(@"IPC took too long to send");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Desktop.Updater
|
|||||||
|
|
||||||
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
|
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
|
||||||
{
|
{
|
||||||
//should we schedule a retry on completion of this check?
|
// should we schedule a retry on completion of this check?
|
||||||
bool scheduleRecheck = true;
|
bool scheduleRecheck = true;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -52,7 +52,7 @@ namespace osu.Desktop.Updater
|
|||||||
|
|
||||||
var info = await updateManager.CheckForUpdate(!useDeltaPatching);
|
var info = await updateManager.CheckForUpdate(!useDeltaPatching);
|
||||||
if (info.ReleasesToApply.Count == 0)
|
if (info.ReleasesToApply.Count == 0)
|
||||||
//no updates available. bail and retry later.
|
// no updates available. bail and retry later.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (notification == null)
|
if (notification == null)
|
||||||
@ -81,8 +81,8 @@ namespace osu.Desktop.Updater
|
|||||||
{
|
{
|
||||||
logger.Add(@"delta patching failed; will attempt full download!");
|
logger.Add(@"delta patching failed; will attempt full download!");
|
||||||
|
|
||||||
//could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
|
// could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
|
||||||
//try again without deltas.
|
// try again without deltas.
|
||||||
checkForUpdateAsync(false, notification);
|
checkForUpdateAsync(false, notification);
|
||||||
scheduleRecheck = false;
|
scheduleRecheck = false;
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ namespace osu.Desktop.Updater
|
|||||||
{
|
{
|
||||||
if (scheduleRecheck)
|
if (scheduleRecheck)
|
||||||
{
|
{
|
||||||
//check again in 30 minutes.
|
// check again in 30 minutes.
|
||||||
Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30);
|
Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.IO.Archives;
|
using osu.Game.IO.Archives;
|
||||||
using osu.Game.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
namespace osu.Game.Benchmarks
|
namespace osu.Game.Benchmarks
|
||||||
{
|
{
|
||||||
@ -18,8 +18,8 @@ namespace osu.Game.Benchmarks
|
|||||||
|
|
||||||
public override void SetUp()
|
public override void SetUp()
|
||||||
{
|
{
|
||||||
using (var resources = new DllResourceStore(OsuResources.ResourceAssembly))
|
using (var resources = new DllResourceStore(typeof(TestResources).Assembly))
|
||||||
using (var archive = resources.GetStream("Beatmaps/241526 Soleily - Renatus.osz"))
|
using (var archive = resources.GetStream("Resources/Archives/241526 Soleily - Renatus.osz"))
|
||||||
using (var reader = new ZipArchiveReader(archive))
|
using (var reader = new ZipArchiveReader(archive))
|
||||||
reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream);
|
reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\osu.Game.Tests\osu.Game.Tests.csproj" />
|
||||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||||
|
|
||||||
[TestCase(4.2058561036909863d, "diffcalc-test")]
|
[TestCase(4.050601681491468d, "diffcalc-test")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
|
36
osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs
Normal file
36
osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.IO.Stores;
|
||||||
|
using osu.Game.Rulesets.Catch.Skinning;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class CatchSkinColourDecodingTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestCatchSkinColourDecoding()
|
||||||
|
{
|
||||||
|
var store = new NamespacedResourceStore<byte[]>(new DllResourceStore(GetType().Assembly), "Resources/special-skin");
|
||||||
|
var rawSkin = new TestLegacySkin(new SkinInfo { Name = "special-skin" }, store);
|
||||||
|
var skin = new CatchLegacySkinTransformer(rawSkin);
|
||||||
|
|
||||||
|
Assert.AreEqual(new Color4(232, 185, 35, 255), skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value);
|
||||||
|
Assert.AreEqual(new Color4(232, 74, 35, 255), skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value);
|
||||||
|
Assert.AreEqual(new Color4(0, 255, 255, 255), skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashFruit)?.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestLegacySkin : LegacySkin
|
||||||
|
{
|
||||||
|
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage)
|
||||||
|
// Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null).
|
||||||
|
: base(skin, storage, null, "skin.ini")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Game.Rulesets.Catch.Skinning;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
public abstract class CatchSkinnableTestScene : SkinnableTestScene
|
public abstract class CatchSkinnableTestScene : SkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(CatchRuleset),
|
|
||||||
typeof(CatchLegacySkinTransformer),
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override Ruleset CreateRulesetForSkinProvider() => new CatchRuleset();
|
protected override Ruleset CreateRulesetForSkinProvider() => new CatchRuleset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
[CatchTheBeat]
|
||||||
|
HyperDash: 232,185,35
|
||||||
|
HyperDashFruit: 0,255,255
|
||||||
|
HyperDashAfterImage: 232,74,35
|
@ -1,30 +1,15 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneBananaShower : TestSceneCatchPlayer
|
public class TestSceneBananaShower : TestSceneCatchPlayer
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(BananaShower),
|
|
||||||
typeof(Banana),
|
|
||||||
typeof(DrawableBananaShower),
|
|
||||||
typeof(DrawableBanana),
|
|
||||||
|
|
||||||
typeof(CatchRuleset),
|
|
||||||
typeof(DrawableCatchRuleset),
|
|
||||||
};
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBananaShower()
|
public void TestBananaShower()
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
@ -11,11 +9,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneCatchPlayer : PlayerTestScene
|
public class TestSceneCatchPlayer : PlayerTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(CatchRuleset),
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,26 +4,18 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneCatcher : CatchSkinnableTestScene
|
public class TestSceneCatcher : CatchSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
|
||||||
{
|
|
||||||
typeof(CatcherArea),
|
|
||||||
typeof(CatcherSprite)
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new Catcher
|
SetContents(() => new Catcher(new Container())
|
||||||
{
|
{
|
||||||
RelativePositionAxes = Axes.None,
|
RelativePositionAxes = Axes.None,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -22,15 +21,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
public class TestSceneDrawableHitObjects : OsuTestScene
|
public class TestSceneDrawableHitObjects : OsuTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(Catcher),
|
|
||||||
typeof(DrawableCatchRuleset),
|
|
||||||
typeof(DrawableFruit),
|
|
||||||
typeof(DrawableJuiceStream),
|
|
||||||
typeof(DrawableBanana)
|
|
||||||
};
|
|
||||||
|
|
||||||
private DrawableCatchRuleset drawableRuleset;
|
private DrawableCatchRuleset drawableRuleset;
|
||||||
private double playfieldTime => drawableRuleset.Playfield.Time.Current;
|
private double playfieldTime => drawableRuleset.Playfield.Time.Current;
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Rulesets.Catch.Mods;
|
using osu.Game.Rulesets.Catch.Mods;
|
||||||
|
|
||||||
@ -11,8 +8,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
public class TestSceneDrawableHitObjectsHidden : TestSceneDrawableHitObjects
|
public class TestSceneDrawableHitObjectsHidden : TestSceneDrawableHitObjects
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(CatchModHidden) }).ToList();
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
|
@ -2,13 +2,10 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
@ -16,22 +13,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneFruitObjects : CatchSkinnableTestScene
|
public class TestSceneFruitObjects : CatchSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
|
||||||
{
|
|
||||||
typeof(CatchHitObject),
|
|
||||||
typeof(Fruit),
|
|
||||||
typeof(FruitPiece),
|
|
||||||
typeof(Droplet),
|
|
||||||
typeof(Banana),
|
|
||||||
typeof(BananaShower),
|
|
||||||
typeof(DrawableCatchHitObject),
|
|
||||||
typeof(DrawableFruit),
|
|
||||||
typeof(DrawableDroplet),
|
|
||||||
typeof(DrawableBanana),
|
|
||||||
typeof(DrawableBananaShower),
|
|
||||||
typeof(Pulp),
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
@ -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;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
@ -17,10 +16,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneHyperDash : TestSceneCatchPlayer
|
public class TestSceneHyperDash : TestSceneCatchPlayer
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(CatcherArea),
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override bool Autoplay => true;
|
protected override bool Autoplay => true;
|
||||||
|
|
||||||
|
@ -26,6 +26,48 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private SkinManager skins { get; set; }
|
private SkinManager skins { get; set; }
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDefaultCatcherColour()
|
||||||
|
{
|
||||||
|
var skin = new TestSkin();
|
||||||
|
|
||||||
|
checkHyperDashCatcherColour(skin, Catcher.DEFAULT_HYPER_DASH_COLOUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCustomCatcherColour()
|
||||||
|
{
|
||||||
|
var skin = new TestSkin
|
||||||
|
{
|
||||||
|
HyperDashColour = Color4.Goldenrod
|
||||||
|
};
|
||||||
|
|
||||||
|
checkHyperDashCatcherColour(skin, skin.HyperDashColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCustomEndGlowColour()
|
||||||
|
{
|
||||||
|
var skin = new TestSkin
|
||||||
|
{
|
||||||
|
HyperDashAfterImageColour = Color4.Lime
|
||||||
|
};
|
||||||
|
|
||||||
|
checkHyperDashCatcherColour(skin, Catcher.DEFAULT_HYPER_DASH_COLOUR, skin.HyperDashAfterImageColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCustomEndGlowColourPriority()
|
||||||
|
{
|
||||||
|
var skin = new TestSkin
|
||||||
|
{
|
||||||
|
HyperDashColour = Color4.Goldenrod,
|
||||||
|
HyperDashAfterImageColour = Color4.Lime
|
||||||
|
};
|
||||||
|
|
||||||
|
checkHyperDashCatcherColour(skin, skin.HyperDashColour, skin.HyperDashAfterImageColour);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDefaultFruitColour()
|
public void TestDefaultFruitColour()
|
||||||
{
|
{
|
||||||
@ -68,6 +110,38 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
checkHyperDashFruitColour(skin, skin.HyperDashColour);
|
checkHyperDashFruitColour(skin, skin.HyperDashColour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null)
|
||||||
|
{
|
||||||
|
CatcherArea catcherArea = null;
|
||||||
|
CatcherTrailDisplay trails = null;
|
||||||
|
|
||||||
|
AddStep("create hyper-dashing catcher", () =>
|
||||||
|
{
|
||||||
|
Child = setupSkinHierarchy(catcherArea = new CatcherArea
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(4f),
|
||||||
|
}, skin);
|
||||||
|
|
||||||
|
trails = catcherArea.OfType<CatcherTrailDisplay>().Single();
|
||||||
|
catcherArea.MovableCatcher.SetHyperDashState(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("catcher colour is correct", () => catcherArea.MovableCatcher.Colour == expectedCatcherColour);
|
||||||
|
|
||||||
|
AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour);
|
||||||
|
AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour));
|
||||||
|
|
||||||
|
AddStep("finish hyper-dashing", () =>
|
||||||
|
{
|
||||||
|
catcherArea.MovableCatcher.SetHyperDashState(1);
|
||||||
|
catcherArea.MovableCatcher.FinishTransforms();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("catcher colour returned to white", () => catcherArea.MovableCatcher.Colour == Color4.White);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkHyperDashFruitColour(ISkin skin, Color4 expectedColour)
|
private void checkHyperDashFruitColour(ISkin skin, Color4 expectedColour)
|
||||||
{
|
{
|
||||||
DrawableFruit drawableFruit = null;
|
DrawableFruit drawableFruit = null;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
|
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
case IHasCurve curveData:
|
case IHasPathWithRepeats curveData:
|
||||||
return new JuiceStream
|
return new JuiceStream
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0
|
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0
|
||||||
}.Yield();
|
}.Yield();
|
||||||
|
|
||||||
case IHasEndTime endTime:
|
case IHasDuration endTime:
|
||||||
return new BananaShower
|
return new BananaShower
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
{
|
{
|
||||||
public class CatchDifficultyCalculator : DifficultyCalculator
|
public class CatchDifficultyCalculator : DifficultyCalculator
|
||||||
{
|
{
|
||||||
private const double star_scaling_factor = 0.145;
|
private const double star_scaling_factor = 0.153;
|
||||||
|
|
||||||
protected override int SectionLength => 750;
|
protected override int SectionLength => 750;
|
||||||
|
|
||||||
@ -73,6 +73,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
{
|
{
|
||||||
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
||||||
|
|
||||||
|
// For circle sizes above 5.5, reduce the catcher width further to simulate imperfect gameplay.
|
||||||
|
halfCatcherWidth *= 1 - (Math.Max(0, beatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5.5f) * 0.0625f);
|
||||||
|
|
||||||
return new Skill[]
|
return new Skill[]
|
||||||
{
|
{
|
||||||
new Movement(halfCatcherWidth),
|
new Movement(halfCatcherWidth),
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Scoring.Legacy;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||||
{
|
{
|
||||||
@ -34,11 +34,11 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
{
|
{
|
||||||
mods = Score.Mods;
|
mods = Score.Mods;
|
||||||
|
|
||||||
fruitsHit = Score?.GetCount300() ?? Score.Statistics[HitResult.Perfect];
|
fruitsHit = Score.Statistics.GetOrDefault(HitResult.Perfect);
|
||||||
ticksHit = Score?.GetCount100() ?? 0;
|
ticksHit = Score.Statistics.GetOrDefault(HitResult.LargeTickHit);
|
||||||
tinyTicksHit = Score?.GetCount50() ?? 0;
|
tinyTicksHit = Score.Statistics.GetOrDefault(HitResult.SmallTickHit);
|
||||||
tinyTicksMissed = Score?.GetCountKatu() ?? 0;
|
tinyTicksMissed = Score.Statistics.GetOrDefault(HitResult.SmallTickMiss);
|
||||||
misses = Score.Statistics[HitResult.Miss];
|
misses = Score.Statistics.GetOrDefault(HitResult.Miss);
|
||||||
|
|
||||||
// Don't count scores made with supposedly unranked mods
|
// Don't count scores made with supposedly unranked mods
|
||||||
if (mods.Any(m => !m.Ranked))
|
if (mods.Any(m => !m.Ranked))
|
||||||
@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
|
|
||||||
// Longer maps are worth more
|
// Longer maps are worth more
|
||||||
double lengthBonus =
|
double lengthBonus =
|
||||||
0.95 + 0.4 * Math.Min(1.0, numTotalHits / 3000.0) +
|
0.95 + 0.3 * Math.Min(1.0, numTotalHits / 2500.0) +
|
||||||
(numTotalHits > 3000 ? Math.Log10(numTotalHits / 3000.0) * 0.5 : 0.0);
|
(numTotalHits > 2500 ? Math.Log10(numTotalHits / 2500.0) * 0.475 : 0.0);
|
||||||
|
|
||||||
// Longer maps are worth more
|
// Longer maps are worth more
|
||||||
value *= lengthBonus;
|
value *= lengthBonus;
|
||||||
@ -63,19 +63,28 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
|
|
||||||
// Combo scaling
|
// Combo scaling
|
||||||
if (Attributes.MaxCombo > 0)
|
if (Attributes.MaxCombo > 0)
|
||||||
value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
|
value *= Math.Min(Math.Pow(Score.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
|
||||||
|
|
||||||
|
double approachRate = Attributes.ApproachRate;
|
||||||
double approachRateFactor = 1.0;
|
double approachRateFactor = 1.0;
|
||||||
if (Attributes.ApproachRate > 9.0)
|
if (approachRate > 9.0)
|
||||||
approachRateFactor += 0.1 * (Attributes.ApproachRate - 9.0); // 10% for each AR above 9
|
approachRateFactor += 0.1 * (approachRate - 9.0); // 10% for each AR above 9
|
||||||
else if (Attributes.ApproachRate < 8.0)
|
if (approachRate > 10.0)
|
||||||
approachRateFactor += 0.025 * (8.0 - Attributes.ApproachRate); // 2.5% for each AR below 8
|
approachRateFactor += 0.1 * (approachRate - 10.0); // Additional 10% at AR 11, 30% total
|
||||||
|
else if (approachRate < 8.0)
|
||||||
|
approachRateFactor += 0.025 * (8.0 - approachRate); // 2.5% for each AR below 8
|
||||||
|
|
||||||
value *= approachRateFactor;
|
value *= approachRateFactor;
|
||||||
|
|
||||||
if (mods.Any(m => m is ModHidden))
|
if (mods.Any(m => m is ModHidden))
|
||||||
// Hiddens gives nothing on max approach rate, and more the lower it is
|
{
|
||||||
value *= 1.05 + 0.075 * (10.0 - Math.Min(10.0, Attributes.ApproachRate)); // 7.5% for each AR below 10
|
value *= 1.05 + 0.075 * (10.0 - Math.Min(10.0, Attributes.ApproachRate)); // 7.5% for each AR below 10
|
||||||
|
// Hiddens gives almost nothing on max approach rate, and more the lower it is
|
||||||
|
if (approachRate <= 10.0)
|
||||||
|
value *= 1.05 + 0.075 * (10.0 - approachRate); // 7.5% for each AR below 10
|
||||||
|
else if (approachRate > 10.0)
|
||||||
|
value *= 1.01 + 0.04 * (11.0 - Math.Min(11.0, approachRate)); // 5% at AR 10, 1% at AR 11
|
||||||
|
}
|
||||||
|
|
||||||
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.
|
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
|
||||||
@ -91,7 +100,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float accuracy() => totalHits() == 0 ? 0 : Math.Clamp((float)totalSuccessfulHits() / totalHits(), 0, 1);
|
private double accuracy() => totalHits() == 0 ? 0 : Math.Clamp((double)totalSuccessfulHits() / totalHits(), 0, 1);
|
||||||
private int totalHits() => tinyTicksHit + ticksHit + fruitsHit + misses + tinyTicksMissed;
|
private int totalHits() => tinyTicksHit + ticksHit + fruitsHit + misses + tinyTicksMissed;
|
||||||
private int totalSuccessfulHits() => tinyTicksHit + ticksHit + fruitsHit;
|
private int totalSuccessfulHits() => tinyTicksHit + ticksHit + fruitsHit;
|
||||||
private int totalComboHits() => misses + ticksHit + fruitsHit;
|
private int totalComboHits() => misses + ticksHit + fruitsHit;
|
||||||
|
@ -21,10 +21,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
public readonly float LastNormalizedPosition;
|
public readonly float LastNormalizedPosition;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Milliseconds elapsed since the start time of the previous <see cref="CatchDifficultyHitObject"/>, with a minimum of 25ms.
|
/// Milliseconds elapsed since the start time of the previous <see cref="CatchDifficultyHitObject"/>, with a minimum of 40ms.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly double StrainTime;
|
public readonly double StrainTime;
|
||||||
|
|
||||||
|
public readonly double ClockRate;
|
||||||
|
|
||||||
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
||||||
: base(hitObject, lastObject, clockRate)
|
: base(hitObject, lastObject, clockRate)
|
||||||
{
|
{
|
||||||
@ -34,8 +36,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
NormalizedPosition = BaseObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
NormalizedPosition = BaseObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
||||||
LastNormalizedPosition = LastObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
LastNormalizedPosition = LastObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
||||||
|
|
||||||
// Every strain interval is hard capped at the equivalent of 600 BPM streaming speed as a safety measure
|
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
||||||
StrainTime = Math.Max(25, DeltaTime);
|
StrainTime = Math.Max(40, DeltaTime);
|
||||||
|
ClockRate = clockRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
{
|
{
|
||||||
private const float absolute_player_positioning_error = 16f;
|
private const float absolute_player_positioning_error = 16f;
|
||||||
private const float normalized_hitobject_radius = 41.0f;
|
private const float normalized_hitobject_radius = 41.0f;
|
||||||
private const double direction_change_bonus = 12.5;
|
private const double direction_change_bonus = 21.0;
|
||||||
|
|
||||||
protected override double SkillMultiplier => 850;
|
protected override double SkillMultiplier => 900;
|
||||||
protected override double StrainDecayBase => 0.2;
|
protected override double StrainDecayBase => 0.2;
|
||||||
|
|
||||||
protected override double DecayWeight => 0.94;
|
protected override double DecayWeight => 0.94;
|
||||||
@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
|
|
||||||
private float? lastPlayerPosition;
|
private float? lastPlayerPosition;
|
||||||
private float lastDistanceMoved;
|
private float lastDistanceMoved;
|
||||||
|
private double lastStrainTime;
|
||||||
|
|
||||||
public Movement(float halfCatcherWidth)
|
public Movement(float halfCatcherWidth)
|
||||||
{
|
{
|
||||||
@ -45,47 +46,47 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
|
|
||||||
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
||||||
|
|
||||||
double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 500;
|
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate);
|
||||||
double sqrtStrain = Math.Sqrt(catchCurrent.StrainTime);
|
|
||||||
|
|
||||||
double bonus = 0;
|
double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
|
||||||
|
double sqrtStrain = Math.Sqrt(weightedStrainTime);
|
||||||
|
|
||||||
// Direction changes give an extra point!
|
double edgeDashBonus = 0;
|
||||||
|
|
||||||
|
// Direction change bonus.
|
||||||
if (Math.Abs(distanceMoved) > 0.1)
|
if (Math.Abs(distanceMoved) > 0.1)
|
||||||
{
|
{
|
||||||
if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved))
|
if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved))
|
||||||
{
|
{
|
||||||
double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(distanceMoved)) / absolute_player_positioning_error;
|
double bonusFactor = Math.Min(50, Math.Abs(distanceMoved)) / 50;
|
||||||
|
double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.38);
|
||||||
|
|
||||||
distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor;
|
distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 16) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0);
|
||||||
|
|
||||||
// Bonus for tougher direction switches and "almost" hyperdashes at this point
|
|
||||||
if (catchCurrent.LastObject.DistanceToHyperDash <= 10 / CatchPlayfield.BASE_WIDTH)
|
|
||||||
bonus = 0.3 * bonusFactor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base bonus for every movement, giving some weight to streams.
|
// Base bonus for every movement, giving some weight to streams.
|
||||||
distanceAddition += 7.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain;
|
distanceAddition += 12.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bonus for "almost" hyperdashes at corner points
|
// Bonus for edge dashes.
|
||||||
if (catchCurrent.LastObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
|
if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f / CatchPlayfield.BASE_WIDTH)
|
||||||
{
|
{
|
||||||
if (!catchCurrent.LastObject.HyperDash)
|
if (!catchCurrent.LastObject.HyperDash)
|
||||||
bonus += 1.0;
|
edgeDashBonus += 5.7;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// After a hyperdash we ARE in the correct position. Always!
|
// After a hyperdash we ARE in the correct position. Always!
|
||||||
playerPosition = catchCurrent.NormalizedPosition;
|
playerPosition = catchCurrent.NormalizedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
distanceAddition *= 1.0 + bonus * ((10 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10);
|
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPlayerPosition = playerPosition;
|
lastPlayerPosition = playerPosition;
|
||||||
lastDistanceMoved = distanceMoved;
|
lastDistanceMoved = distanceMoved;
|
||||||
|
lastStrainTime = catchCurrent.StrainTime;
|
||||||
|
|
||||||
return distanceAddition / catchCurrent.StrainTime;
|
return distanceAddition / weightedStrainTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
//disable keyboard controls
|
// disable keyboard controls
|
||||||
public bool OnPressed(CatchAction action) => true;
|
public bool OnPressed(CatchAction action) => true;
|
||||||
|
|
||||||
public void OnReleased(CatchAction action)
|
public void OnReleased(CatchAction action)
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// 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.Threading;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class BananaShower : CatchHitObject, IHasEndTime
|
public class BananaShower : CatchHitObject, IHasDuration
|
||||||
{
|
{
|
||||||
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
||||||
|
|
||||||
@ -14,13 +15,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects()
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects(cancellationToken);
|
||||||
createBananas();
|
createBananas(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createBananas()
|
private void createBananas(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
double spacing = Duration;
|
double spacing = Duration;
|
||||||
while (spacing > 100)
|
while (spacing > 100)
|
||||||
@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
for (double i = StartTime; i <= EndTime; i += spacing)
|
for (double i = StartTime; i <= EndTime; i += spacing)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
AddNested(new Banana
|
AddNested(new Banana
|
||||||
{
|
{
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -13,7 +14,7 @@ using osu.Game.Rulesets.Objects.Types;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class JuiceStream : CatchHitObject, IHasCurve
|
public class JuiceStream : CatchHitObject, IHasPathWithRepeats
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
||||||
@ -45,9 +46,9 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects()
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects(cancellationToken);
|
||||||
|
|
||||||
var dropletSamples = Samples.Select(s => new HitSampleInfo
|
var dropletSamples = Samples.Select(s => new HitSampleInfo
|
||||||
{
|
{
|
||||||
@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
SliderEventDescriptor? lastEvent = null;
|
SliderEventDescriptor? lastEvent = null;
|
||||||
|
|
||||||
foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset))
|
foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken))
|
||||||
{
|
{
|
||||||
// generate tiny droplets since the last point
|
// generate tiny droplets since the last point
|
||||||
if (lastEvent != null)
|
if (lastEvent != null)
|
||||||
@ -73,6 +74,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny)
|
for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
AddNested(new TinyDroplet
|
AddNested(new TinyDroplet
|
||||||
{
|
{
|
||||||
StartTime = t + lastEvent.Value.Time,
|
StartTime = t + lastEvent.Value.Time,
|
||||||
@ -112,15 +115,15 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public double EndTime
|
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
|
||||||
|
|
||||||
|
public double Duration
|
||||||
{
|
{
|
||||||
get => StartTime + this.SpanCount() * Path.Distance / Velocity;
|
get => this.SpanCount() * Path.Distance / Velocity;
|
||||||
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
|
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
|
public double EndTime => StartTime + Duration;
|
||||||
|
|
||||||
public double Duration => EndTime - StartTime;
|
|
||||||
|
|
||||||
private readonly SliderPath path = new SliderPath();
|
private readonly SliderPath path = new SliderPath();
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
|
|
||||||
if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X)
|
if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X)
|
||||||
{
|
{
|
||||||
//we are already in the correct range.
|
// we are already in the correct range.
|
||||||
lastTime = h.StartTime;
|
lastTime = h.StartTime;
|
||||||
addFrame(h.StartTime, lastPosition);
|
addFrame(h.StartTime, lastPosition);
|
||||||
return;
|
return;
|
||||||
@ -72,14 +72,14 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
}
|
}
|
||||||
else if (dashRequired)
|
else if (dashRequired)
|
||||||
{
|
{
|
||||||
//we do a movement in two parts - the dash part then the normal part...
|
// we do a movement in two parts - the dash part then the normal part...
|
||||||
double timeAtNormalSpeed = positionChange / movement_speed;
|
double timeAtNormalSpeed = positionChange / movement_speed;
|
||||||
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
||||||
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
||||||
|
|
||||||
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
|
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
|
||||||
|
|
||||||
//dash movement
|
// dash movement
|
||||||
addFrame(h.StartTime - timeAvailable + 1, lastPosition, true);
|
addFrame(h.StartTime - timeAvailable + 1, lastPosition, true);
|
||||||
addFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition);
|
addFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition);
|
||||||
addFrame(h.StartTime, h.X);
|
addFrame(h.StartTime, h.X);
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
|||||||
{
|
{
|
||||||
private readonly ISkin source;
|
private readonly ISkin source;
|
||||||
|
|
||||||
public CatchLegacySkinTransformer(ISkinSource source)
|
public CatchLegacySkinTransformer(ISkin source)
|
||||||
{
|
{
|
||||||
this.source = source;
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
@ -3,26 +3,37 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Animations;
|
using osu.Framework.Graphics.Animations;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Catch.Skinning;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
public class Catcher : SkinReloadableDrawable, IKeyBindingHandler<CatchAction>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail
|
||||||
|
/// and end glow/after-image during a hyper-dash.
|
||||||
|
/// </summary>
|
||||||
public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red;
|
public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The duration between transitioning to hyper-dash state.
|
||||||
|
/// </summary>
|
||||||
|
public const double HYPER_DASH_TRANSITION_DURATION = 180;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we are hyper-dashing or not.
|
/// Whether we are hyper-dashing or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -35,7 +46,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public Container ExplodingFruitTarget;
|
public Container ExplodingFruitTarget;
|
||||||
|
|
||||||
public Container AdditiveTarget;
|
[NotNull]
|
||||||
|
private readonly Container trailsTarget;
|
||||||
|
|
||||||
|
private CatcherTrailDisplay trails;
|
||||||
|
|
||||||
public CatcherAnimationState CurrentState { get; private set; }
|
public CatcherAnimationState CurrentState { get; private set; }
|
||||||
|
|
||||||
@ -44,33 +58,23 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const float allowed_catch_range = 0.8f;
|
private const float allowed_catch_range = 0.8f;
|
||||||
|
|
||||||
protected bool Dashing
|
/// <summary>
|
||||||
|
/// The drawable catcher for <see cref="CurrentState"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal Drawable CurrentDrawableCatcher => currentCatcher.Drawable;
|
||||||
|
|
||||||
|
private bool dashing;
|
||||||
|
|
||||||
|
public bool Dashing
|
||||||
{
|
{
|
||||||
get => dashing;
|
get => dashing;
|
||||||
set
|
protected set
|
||||||
{
|
{
|
||||||
if (value == dashing) return;
|
if (value == dashing) return;
|
||||||
|
|
||||||
dashing = value;
|
dashing = value;
|
||||||
|
|
||||||
Trail |= dashing;
|
updateTrailVisibility();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Activate or deactivate the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
|
|
||||||
/// </summary>
|
|
||||||
protected bool Trail
|
|
||||||
{
|
|
||||||
get => trail;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == trail || AdditiveTarget == null) return;
|
|
||||||
|
|
||||||
trail = value;
|
|
||||||
|
|
||||||
if (Trail)
|
|
||||||
beginTrail();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,18 +91,19 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
private CatcherSprite currentCatcher;
|
private CatcherSprite currentCatcher;
|
||||||
|
|
||||||
|
private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
|
||||||
private int currentDirection;
|
private int currentDirection;
|
||||||
|
|
||||||
private bool dashing;
|
|
||||||
|
|
||||||
private bool trail;
|
|
||||||
|
|
||||||
private double hyperDashModifier = 1;
|
private double hyperDashModifier = 1;
|
||||||
private int hyperDashDirection;
|
private int hyperDashDirection;
|
||||||
private float hyperDashTargetPosition;
|
private float hyperDashTargetPosition;
|
||||||
|
|
||||||
public Catcher(BeatmapDifficulty difficulty = null)
|
public Catcher([NotNull] Container trailsTarget, BeatmapDifficulty difficulty = null)
|
||||||
{
|
{
|
||||||
|
this.trailsTarget = trailsTarget;
|
||||||
|
|
||||||
RelativePositionAxes = Axes.X;
|
RelativePositionAxes = Axes.X;
|
||||||
X = 0.5f;
|
X = 0.5f;
|
||||||
|
|
||||||
@ -114,7 +119,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
caughtFruit = new Container<DrawableHitObject>
|
caughtFruit = new Container<DrawableHitObject>
|
||||||
{
|
{
|
||||||
@ -138,6 +143,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trailsTarget.Add(trails = new CatcherTrailDisplay(this));
|
||||||
|
|
||||||
updateCatcher();
|
updateCatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +192,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
caughtFruit.Add(fruit);
|
caughtFruit.Add(fruit);
|
||||||
|
|
||||||
Add(new HitExplosion(fruit)
|
AddInternal(new HitExplosion(fruit)
|
||||||
{
|
{
|
||||||
X = fruit.X,
|
X = fruit.X,
|
||||||
Scale = new Vector2(fruit.HitObject.Scale)
|
Scale = new Vector2(fruit.HitObject.Scale)
|
||||||
@ -240,8 +247,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyper-dashing.</param>
|
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyper-dashing.</param>
|
||||||
public void SetHyperDashState(double modifier = 1, float targetPosition = -1)
|
public void SetHyperDashState(double modifier = 1, float targetPosition = -1)
|
||||||
{
|
{
|
||||||
const float hyper_dash_transition_length = 180;
|
|
||||||
|
|
||||||
var wasHyperDashing = HyperDashing;
|
var wasHyperDashing = HyperDashing;
|
||||||
|
|
||||||
if (modifier <= 1 || X == targetPosition)
|
if (modifier <= 1 || X == targetPosition)
|
||||||
@ -250,11 +255,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
hyperDashDirection = 0;
|
hyperDashDirection = 0;
|
||||||
|
|
||||||
if (wasHyperDashing)
|
if (wasHyperDashing)
|
||||||
{
|
runHyperDashStateTransition(false);
|
||||||
this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint);
|
|
||||||
this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint);
|
|
||||||
Trail &= Dashing;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -264,20 +265,32 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
if (!wasHyperDashing)
|
if (!wasHyperDashing)
|
||||||
{
|
{
|
||||||
this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint);
|
trails.DisplayEndGlow();
|
||||||
this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint);
|
runHyperDashStateTransition(true);
|
||||||
Trail = true;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var hyperDashEndGlow = createAdditiveSprite();
|
private void runHyperDashStateTransition(bool hyperDashing)
|
||||||
|
{
|
||||||
|
trails.HyperDashTrailsColour = hyperDashColour;
|
||||||
|
trails.EndGlowSpritesColour = hyperDashEndGlowColour;
|
||||||
|
updateTrailVisibility();
|
||||||
|
|
||||||
hyperDashEndGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In);
|
if (hyperDashing)
|
||||||
hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.95f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In);
|
{
|
||||||
hyperDashEndGlow.FadeOut(1200);
|
this.FadeColour(hyperDashColour, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
||||||
hyperDashEndGlow.Expire(true);
|
this.FadeTo(0.2f, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.FadeColour(Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
this.FadeTo(1f, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing;
|
||||||
|
|
||||||
public bool OnPressed(CatchAction action)
|
public bool OnPressed(CatchAction action)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action)
|
||||||
@ -366,6 +379,21 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
|
{
|
||||||
|
base.SkinChanged(skin, allowFallback);
|
||||||
|
|
||||||
|
hyperDashColour =
|
||||||
|
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
||||||
|
DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
|
||||||
|
hyperDashEndGlowColour =
|
||||||
|
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value ??
|
||||||
|
hyperDashColour;
|
||||||
|
|
||||||
|
runHyperDashStateTransition(HyperDashing);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -411,22 +439,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
(currentCatcher.Drawable as IFramedAnimation)?.GotoFrame(0);
|
(currentCatcher.Drawable as IFramedAnimation)?.GotoFrame(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beginTrail()
|
|
||||||
{
|
|
||||||
if (!dashing && !HyperDashing)
|
|
||||||
{
|
|
||||||
Trail = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var additive = createAdditiveSprite();
|
|
||||||
|
|
||||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
|
|
||||||
additive.Expire(true);
|
|
||||||
|
|
||||||
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateState(CatcherAnimationState state)
|
private void updateState(CatcherAnimationState state)
|
||||||
{
|
{
|
||||||
if (CurrentState == state)
|
if (CurrentState == state)
|
||||||
@ -436,25 +448,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
updateCatcher();
|
updateCatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CatcherTrailSprite createAdditiveSprite()
|
|
||||||
{
|
|
||||||
var tex = (currentCatcher.Drawable as TextureAnimation)?.CurrentFrame ?? ((Sprite)currentCatcher.Drawable).Texture;
|
|
||||||
|
|
||||||
var sprite = new CatcherTrailSprite(tex)
|
|
||||||
{
|
|
||||||
Anchor = Anchor,
|
|
||||||
Scale = Scale,
|
|
||||||
Colour = HyperDashing ? Color4.Red : Color4.White,
|
|
||||||
Blending = BlendingParameters.Additive,
|
|
||||||
RelativePositionAxes = RelativePositionAxes,
|
|
||||||
Position = Position
|
|
||||||
};
|
|
||||||
|
|
||||||
AdditiveTarget?.Add(sprite);
|
|
||||||
|
|
||||||
return sprite;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeFromPlateWithTransform(DrawableHitObject fruit, Action<DrawableHitObject> action)
|
private void removeFromPlateWithTransform(DrawableHitObject fruit, Action<DrawableHitObject> action)
|
||||||
{
|
{
|
||||||
if (ExplodingFruitTarget != null)
|
if (ExplodingFruitTarget != null)
|
||||||
|
@ -33,10 +33,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = CATCHER_SIZE;
|
Height = CATCHER_SIZE;
|
||||||
Child = MovableCatcher = new Catcher(difficulty)
|
Child = MovableCatcher = new Catcher(this, difficulty);
|
||||||
{
|
|
||||||
AdditiveTarget = this,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float GetCatcherSize(BeatmapDifficulty difficulty)
|
public static float GetCatcherSize(BeatmapDifficulty difficulty)
|
||||||
|
135
osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
Normal file
135
osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// 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 JetBrains.Annotations;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Animations;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a component responsible for displaying
|
||||||
|
/// the appropriate catcher trails when requested to.
|
||||||
|
/// </summary>
|
||||||
|
public class CatcherTrailDisplay : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly Catcher catcher;
|
||||||
|
|
||||||
|
private readonly Container<CatcherTrailSprite> dashTrails;
|
||||||
|
private readonly Container<CatcherTrailSprite> hyperDashTrails;
|
||||||
|
private readonly Container<CatcherTrailSprite> endGlowSprites;
|
||||||
|
|
||||||
|
private Color4 hyperDashTrailsColour;
|
||||||
|
|
||||||
|
public Color4 HyperDashTrailsColour
|
||||||
|
{
|
||||||
|
get => hyperDashTrailsColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (hyperDashTrailsColour == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hyperDashTrailsColour = value;
|
||||||
|
hyperDashTrails.FadeColour(hyperDashTrailsColour, Catcher.HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 endGlowSpritesColour;
|
||||||
|
|
||||||
|
public Color4 EndGlowSpritesColour
|
||||||
|
{
|
||||||
|
get => endGlowSpritesColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (endGlowSpritesColour == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
endGlowSpritesColour = value;
|
||||||
|
endGlowSprites.FadeColour(endGlowSpritesColour, Catcher.HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool trail;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to start displaying trails following the catcher.
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayTrail
|
||||||
|
{
|
||||||
|
get => trail;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (trail == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
trail = value;
|
||||||
|
|
||||||
|
if (trail)
|
||||||
|
displayTrail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CatcherTrailDisplay([NotNull] Catcher catcher)
|
||||||
|
{
|
||||||
|
this.catcher = catcher ?? throw new ArgumentNullException(nameof(catcher));
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChildren = new[]
|
||||||
|
{
|
||||||
|
dashTrails = new Container<CatcherTrailSprite> { RelativeSizeAxes = Axes.Both },
|
||||||
|
hyperDashTrails = new Container<CatcherTrailSprite> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
|
||||||
|
endGlowSprites = new Container<CatcherTrailSprite> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Displays a single end-glow catcher sprite.
|
||||||
|
/// </summary>
|
||||||
|
public void DisplayEndGlow()
|
||||||
|
{
|
||||||
|
var endGlow = createTrailSprite(endGlowSprites);
|
||||||
|
|
||||||
|
endGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In);
|
||||||
|
endGlow.ScaleTo(endGlow.Scale * 0.95f).ScaleTo(endGlow.Scale * 1.2f, 1200, Easing.In);
|
||||||
|
endGlow.FadeOut(1200);
|
||||||
|
endGlow.Expire(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayTrail()
|
||||||
|
{
|
||||||
|
if (!DisplayTrail)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sprite = createTrailSprite(catcher.HyperDashing ? hyperDashTrails : dashTrails);
|
||||||
|
|
||||||
|
sprite.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
|
||||||
|
sprite.Expire(true);
|
||||||
|
|
||||||
|
Scheduler.AddDelayed(displayTrail, catcher.HyperDashing ? 25 : 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CatcherTrailSprite createTrailSprite(Container<CatcherTrailSprite> target)
|
||||||
|
{
|
||||||
|
var texture = (catcher.CurrentDrawableCatcher as TextureAnimation)?.CurrentFrame ?? ((Sprite)catcher.CurrentDrawableCatcher).Texture;
|
||||||
|
|
||||||
|
var sprite = new CatcherTrailSprite(texture)
|
||||||
|
{
|
||||||
|
Anchor = catcher.Anchor,
|
||||||
|
Scale = catcher.Scale,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
RelativePositionAxes = catcher.RelativePositionAxes,
|
||||||
|
Position = catcher.Position
|
||||||
|
};
|
||||||
|
|
||||||
|
target.Add(sprite);
|
||||||
|
|
||||||
|
return sprite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,20 +7,18 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Rulesets.Mania.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
[Cached(Type = typeof(IManiaHitObjectComposer))]
|
public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene
|
||||||
public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene, IManiaHitObjectComposer
|
|
||||||
{
|
{
|
||||||
private readonly Column column;
|
private readonly Column column;
|
||||||
|
|
||||||
@ -41,16 +39,20 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
AccentColour = Color4.OrangeRed,
|
AccentColour = Color4.OrangeRed,
|
||||||
Clock = new FramedClock(new StopwatchClock()), // No scroll
|
Clock = new FramedClock(new StopwatchClock()), // No scroll
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
AddStep("change direction", () => ((ScrollingTestContainer)HitObjectContainer).Flip());
|
protected override SnapResult SnapForBlueprint(PlacementBlueprint blueprint)
|
||||||
|
{
|
||||||
|
var time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position);
|
||||||
|
var pos = column.ScreenSpacePositionAtTime(time);
|
||||||
|
|
||||||
|
return new SnapResult(pos, time, column);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };
|
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
protected override void AddHitObject(DrawableHitObject hitObject) => column.Add((DrawableManiaHitObject)hitObject);
|
protected override void AddHitObject(DrawableHitObject hitObject) => column.Add((DrawableManiaHitObject)hitObject);
|
||||||
|
|
||||||
public Column ColumnAt(Vector2 screenSpacePosition) => column;
|
public ManiaPlayfield Playfield => null;
|
||||||
|
|
||||||
public int TotalColumns => 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,25 +4,20 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Rulesets.Mania.Edit;
|
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
[Cached(Type = typeof(IManiaHitObjectComposer))]
|
public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene
|
||||||
public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene, IManiaHitObjectComposer
|
|
||||||
{
|
{
|
||||||
[Cached(Type = typeof(IAdjustableClock))]
|
[Cached(Type = typeof(IAdjustableClock))]
|
||||||
private readonly IAdjustableClock clock = new StopwatchClock();
|
private readonly IAdjustableClock clock = new StopwatchClock();
|
||||||
|
|
||||||
private readonly Column column;
|
|
||||||
|
|
||||||
protected ManiaSelectionBlueprintTestScene()
|
protected ManiaSelectionBlueprintTestScene()
|
||||||
{
|
{
|
||||||
Add(column = new Column(0)
|
Add(new Column(0)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -31,8 +26,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Column ColumnAt(Vector2 screenSpacePosition) => column;
|
public ManiaPlayfield Playfield => null;
|
||||||
|
|
||||||
public int TotalColumns => 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 165 B |
Binary file not shown.
After Width: | Height: | Size: 899 B |
@ -1,15 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
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.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Rulesets.Mania.Skinning;
|
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Rulesets.UI.Scrolling.Algorithms;
|
using osu.Game.Rulesets.UI.Scrolling.Algorithms;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -27,13 +24,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[Cached(Type = typeof(IScrollingInfo))]
|
[Cached(Type = typeof(IScrollingInfo))]
|
||||||
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
||||||
|
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(ManiaRuleset),
|
|
||||||
typeof(ManiaLegacySkinTransformer),
|
|
||||||
typeof(ManiaSettingsSubsection)
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset();
|
protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset();
|
||||||
|
|
||||||
protected ManiaSkinnableTestScene()
|
protected ManiaSkinnableTestScene()
|
||||||
|
@ -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;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -15,12 +14,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
public class TestSceneDrawableJudgement : ManiaSkinnableTestScene
|
public class TestSceneDrawableJudgement : ManiaSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(DrawableJudgement),
|
|
||||||
typeof(DrawableManiaJudgement)
|
|
||||||
};
|
|
||||||
|
|
||||||
public TestSceneDrawableJudgement()
|
public TestSceneDrawableJudgement()
|
||||||
{
|
{
|
||||||
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
|
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
@ -21,12 +18,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneHitExplosion : ManiaSkinnableTestScene
|
public class TestSceneHitExplosion : ManiaSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(DrawableNote),
|
|
||||||
typeof(DrawableManiaHitObject),
|
|
||||||
};
|
|
||||||
|
|
||||||
public TestSceneHitExplosion()
|
public TestSceneHitExplosion()
|
||||||
{
|
{
|
||||||
int runcount = 0;
|
int runcount = 0;
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Mania.Skinning;
|
|
||||||
using osu.Game.Rulesets.Mania.UI.Components;
|
using osu.Game.Rulesets.Mania.UI.Components;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -15,12 +12,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
public class TestSceneKeyArea : ManiaSkinnableTestScene
|
public class TestSceneKeyArea : ManiaSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(DefaultKeyArea),
|
|
||||||
typeof(LegacyKeyArea)
|
|
||||||
};
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.Skinning;
|
|
||||||
using osu.Game.Rulesets.Mania.UI.Components;
|
using osu.Game.Rulesets.Mania.UI.Components;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -14,12 +10,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
public class TestSceneStageBackground : ManiaSkinnableTestScene
|
public class TestSceneStageBackground : ManiaSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
|
||||||
{
|
|
||||||
typeof(DefaultStageBackground),
|
|
||||||
typeof(LegacyStageBackground),
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
@ -1,23 +1,14 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.Skinning;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||||
{
|
{
|
||||||
public class TestSceneStageForeground : ManiaSkinnableTestScene
|
public class TestSceneStageForeground : ManiaSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
|
||||||
{
|
|
||||||
typeof(LegacyStageForeground),
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,6 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mania.UI.Components;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -24,15 +23,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneColumn : ManiaInputTestScene
|
public class TestSceneColumn : ManiaInputTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(Column),
|
|
||||||
typeof(ColumnBackground),
|
|
||||||
typeof(ColumnHitObjectArea),
|
|
||||||
typeof(DefaultKeyArea),
|
|
||||||
typeof(DefaultHitTarget)
|
|
||||||
};
|
|
||||||
|
|
||||||
[Cached(typeof(IReadOnlyList<Mod>))]
|
[Cached(typeof(IReadOnlyList<Mod>))]
|
||||||
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
|
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
|
||||||
|
|
||||||
|
124
osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs
Normal file
124
osu.Game.Rulesets.Mania.Tests/TestSceneManiaBeatSnapGrid.cs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneManiaBeatSnapGrid : EditorClockTestScene
|
||||||
|
{
|
||||||
|
[Cached(typeof(IScrollingInfo))]
|
||||||
|
private ScrollingTestContainer.TestScrollingInfo scrollingInfo = new ScrollingTestContainer.TestScrollingInfo();
|
||||||
|
|
||||||
|
[Cached(typeof(EditorBeatmap))]
|
||||||
|
private EditorBeatmap editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition()));
|
||||||
|
|
||||||
|
private readonly ManiaBeatSnapGrid beatSnapGrid;
|
||||||
|
|
||||||
|
public TestSceneManiaBeatSnapGrid()
|
||||||
|
{
|
||||||
|
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 200 });
|
||||||
|
editorBeatmap.ControlPointInfo.Add(10000, new TimingControlPoint { BeatLength = 200 });
|
||||||
|
|
||||||
|
BeatDivisor.Value = 3;
|
||||||
|
|
||||||
|
// Some sane defaults
|
||||||
|
scrollingInfo.Algorithm.Algorithm = ScrollVisualisationMethod.Constant;
|
||||||
|
scrollingInfo.Direction.Value = ScrollingDirection.Up;
|
||||||
|
scrollingInfo.TimeRange.Value = 1000;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
Playfield = new ManiaPlayfield(new List<StageDefinition>
|
||||||
|
{
|
||||||
|
new StageDefinition { Columns = 4 },
|
||||||
|
new StageDefinition { Columns = 3 }
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Clock = new FramedClock(new StopwatchClock())
|
||||||
|
},
|
||||||
|
new TestHitObjectComposer(Playfield)
|
||||||
|
{
|
||||||
|
Child = beatSnapGrid = new ManiaBeatSnapGrid()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
|
{
|
||||||
|
// We're providing a constant scroll algorithm.
|
||||||
|
float relativePosition = Playfield.Stages[0].HitObjectContainer.ToLocalSpace(e.ScreenSpaceMousePosition).Y / Playfield.Stages[0].HitObjectContainer.DrawHeight;
|
||||||
|
double timeValue = scrollingInfo.TimeRange.Value * relativePosition;
|
||||||
|
|
||||||
|
beatSnapGrid.SelectionTimeRange = (timeValue, timeValue);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManiaPlayfield Playfield { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestHitObjectComposer : HitObjectComposer
|
||||||
|
{
|
||||||
|
public override Playfield Playfield { get; }
|
||||||
|
public override IEnumerable<DrawableHitObject> HitObjects => Enumerable.Empty<DrawableHitObject>();
|
||||||
|
public override bool CursorInPlacementArea => false;
|
||||||
|
|
||||||
|
public TestHitObjectComposer(Playfield playfield)
|
||||||
|
{
|
||||||
|
Playfield = playfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable Child
|
||||||
|
{
|
||||||
|
set => InternalChild = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float GetBeatSnapDistanceAt(double referenceTime)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float DurationToDistance(double referenceTime, double duration)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override double DistanceToDuration(double referenceTime, float distance)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
218
osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs
Normal file
218
osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneManiaHitObjectComposer : EditorClockTestScene
|
||||||
|
{
|
||||||
|
private TestComposer composer;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
BeatDivisor.Value = 8;
|
||||||
|
Clock.Seek(0);
|
||||||
|
|
||||||
|
Child = composer = new TestComposer { RelativeSizeAxes = Axes.Both };
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragOffscreenSelectionVerticallyUpScroll()
|
||||||
|
{
|
||||||
|
DrawableHitObject lastObject = null;
|
||||||
|
double originalTime = 0;
|
||||||
|
Vector2 originalPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
setScrollStep(ScrollingDirection.Up);
|
||||||
|
|
||||||
|
AddStep("seek to last object", () =>
|
||||||
|
{
|
||||||
|
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
||||||
|
originalTime = lastObject.HitObject.StartTime;
|
||||||
|
Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects));
|
||||||
|
|
||||||
|
AddStep("click last object", () =>
|
||||||
|
{
|
||||||
|
originalPosition = lastObject.DrawPosition;
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(lastObject);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move mouse downwards", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(lastObject, new Vector2(0, lastObject.ScreenSpaceDrawQuad.Height * 4));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0));
|
||||||
|
AddAssert("hitobjects moved downwards", () => lastObject.DrawPosition.Y - originalPosition.Y > 0);
|
||||||
|
AddAssert("hitobject has moved time", () => lastObject.HitObject.StartTime == originalTime + 125);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragOffscreenSelectionVerticallyDownScroll()
|
||||||
|
{
|
||||||
|
DrawableHitObject lastObject = null;
|
||||||
|
double originalTime = 0;
|
||||||
|
Vector2 originalPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
setScrollStep(ScrollingDirection.Down);
|
||||||
|
|
||||||
|
AddStep("seek to last object", () =>
|
||||||
|
{
|
||||||
|
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
||||||
|
originalTime = lastObject.HitObject.StartTime;
|
||||||
|
Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects));
|
||||||
|
|
||||||
|
AddStep("click last object", () =>
|
||||||
|
{
|
||||||
|
originalPosition = lastObject.DrawPosition;
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(lastObject);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move mouse upwards", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(lastObject, new Vector2(0, -lastObject.ScreenSpaceDrawQuad.Height * 4));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0));
|
||||||
|
AddAssert("hitobjects moved upwards", () => originalPosition.Y - lastObject.DrawPosition.Y > 0);
|
||||||
|
AddAssert("hitobject has moved time", () => lastObject.HitObject.StartTime == originalTime + 125);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragOffscreenSelectionHorizontally()
|
||||||
|
{
|
||||||
|
DrawableHitObject lastObject = null;
|
||||||
|
Vector2 originalPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
setScrollStep(ScrollingDirection.Down);
|
||||||
|
|
||||||
|
AddStep("seek to last object", () =>
|
||||||
|
{
|
||||||
|
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
||||||
|
Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects));
|
||||||
|
|
||||||
|
AddStep("click last object", () =>
|
||||||
|
{
|
||||||
|
originalPosition = lastObject.DrawPosition;
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(lastObject);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move mouse right", () =>
|
||||||
|
{
|
||||||
|
var firstColumn = composer.Composer.Playfield.GetColumn(0);
|
||||||
|
var secondColumn = composer.Composer.Playfield.GetColumn(1);
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(lastObject, new Vector2(secondColumn.ScreenSpaceDrawQuad.Centre.X - firstColumn.ScreenSpaceDrawQuad.Centre.X + 1, 0));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("hitobjects moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 1));
|
||||||
|
|
||||||
|
// Todo: They'll move vertically by the height of a note since there's no snapping and the selection point is the middle of the note.
|
||||||
|
AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y <= DefaultNotePiece.NOTE_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragHoldNoteSelectionVertically()
|
||||||
|
{
|
||||||
|
setScrollStep(ScrollingDirection.Down);
|
||||||
|
|
||||||
|
AddStep("setup beatmap", () =>
|
||||||
|
{
|
||||||
|
composer.EditorBeatmap.Clear();
|
||||||
|
composer.EditorBeatmap.Add(new HoldNote
|
||||||
|
{
|
||||||
|
Column = 1,
|
||||||
|
EndTime = 200
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
DrawableHoldNote holdNote = null;
|
||||||
|
|
||||||
|
AddStep("grab hold note", () =>
|
||||||
|
{
|
||||||
|
holdNote = this.ChildrenOfType<DrawableHoldNote>().Single();
|
||||||
|
InputManager.MoveMouseTo(holdNote);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move drag upwards", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(holdNote, new Vector2(0, -100));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("head note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.BottomLeft, holdNote.Head.ScreenSpaceDrawQuad.BottomLeft));
|
||||||
|
AddAssert("tail note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.TopLeft, holdNote.Tail.ScreenSpaceDrawQuad.BottomLeft));
|
||||||
|
|
||||||
|
AddAssert("head blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteSelectionBlueprint>().ElementAt(0).DrawPosition == holdNote.Head.DrawPosition);
|
||||||
|
AddAssert("tail blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteSelectionBlueprint>().ElementAt(1).DrawPosition == holdNote.Tail.DrawPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setScrollStep(ScrollingDirection direction)
|
||||||
|
=> AddStep($"set scroll direction = {direction}", () => ((Bindable<ScrollingDirection>)composer.Composer.ScrollingInfo.Direction).Value = direction);
|
||||||
|
|
||||||
|
private class TestComposer : CompositeDrawable
|
||||||
|
{
|
||||||
|
[Cached(typeof(EditorBeatmap))]
|
||||||
|
[Cached(typeof(IBeatSnapProvider))]
|
||||||
|
public readonly EditorBeatmap EditorBeatmap;
|
||||||
|
|
||||||
|
public readonly ManiaHitObjectComposer Composer;
|
||||||
|
|
||||||
|
public TestComposer()
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 }))
|
||||||
|
{
|
||||||
|
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }
|
||||||
|
},
|
||||||
|
Composer = new ManiaHitObjectComposer(new ManiaRuleset())
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
EditorBeatmap.Add(new Note { StartTime = 125 * i });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
public class TestSceneManiaPlayer : PlayerTestScene
|
public class TestSceneManiaPlayer : PlayerTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(ManiaRuleset),
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,59 @@
|
|||||||
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
|
public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
|
||||||
{
|
{
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
this.ChildrenOfType<HitObjectContainer>().ForEach(c => c.Clear());
|
||||||
|
|
||||||
|
ResetPlacement();
|
||||||
|
|
||||||
|
((ScrollingTestContainer)HitObjectContainer).Direction = ScrollingDirection.Down;
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPlaceBeforeCurrentTimeDownwards()
|
||||||
|
{
|
||||||
|
AddStep("move mouse before current time", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Single().ScreenSpaceDrawQuad.BottomLeft - new Vector2(0, 10)));
|
||||||
|
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("note start time < 0", () => getNote().StartTime < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPlaceAfterCurrentTimeDownwards()
|
||||||
|
{
|
||||||
|
AddStep("move mouse after current time", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Single()));
|
||||||
|
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("note start time > 0", () => getNote().StartTime > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Note getNote() => this.ChildrenOfType<DrawableNote>().FirstOrDefault()?.HitObject;
|
||||||
|
|
||||||
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableNote((Note)hitObject);
|
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableNote((Note)hitObject);
|
||||||
protected override PlacementBlueprint CreateBlueprint() => new NotePlacementBlueprint();
|
protected override PlacementBlueprint CreateBlueprint() => new NotePlacementBlueprint();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -30,12 +28,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneNotes : OsuTestScene
|
public class TestSceneNotes : OsuTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(DrawableNote),
|
|
||||||
typeof(DrawableHoldNote)
|
|
||||||
};
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -164,7 +156,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
foreach (var obj in content.OfType<DrawableHitObject>())
|
foreach (var obj in content.OfType<DrawableHitObject>())
|
||||||
{
|
{
|
||||||
if (!(obj.HitObject is IHasEndTime endTime))
|
if (!(obj.HitObject is IHasDuration endTime))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
foreach (var nested in obj.NestedHitObjects)
|
foreach (var nested in obj.NestedHitObjects)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -13,7 +13,6 @@ using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
|||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Game.Audio;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps
|
namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||||
{
|
{
|
||||||
@ -55,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count;
|
float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasDuration) / beatmap.HitObjects.Count;
|
||||||
if (percentSliderOrSpinner < 0.2)
|
if (percentSliderOrSpinner < 0.2)
|
||||||
TargetColumns = 7;
|
TargetColumns = 7;
|
||||||
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
|
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
|
||||||
@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition || h is ManiaHitObject);
|
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition);
|
||||||
|
|
||||||
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original)
|
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original)
|
||||||
{
|
{
|
||||||
@ -176,7 +175,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case IHasEndTime endTimeData:
|
case IHasDuration endTimeData:
|
||||||
{
|
{
|
||||||
conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap);
|
conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap);
|
||||||
|
|
||||||
@ -232,15 +231,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
if (HitObject is IHasEndTime endTimeData)
|
if (HitObject is IHasDuration endTimeData)
|
||||||
{
|
{
|
||||||
pattern.Add(new HoldNote
|
pattern.Add(new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
Duration = endTimeData.Duration,
|
Duration = endTimeData.Duration,
|
||||||
Column = column,
|
Column = column,
|
||||||
Head = { Samples = sampleInfoListAt(HitObject.StartTime) },
|
Samples = HitObject.Samples,
|
||||||
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) },
|
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (HitObject is IHasXPosition)
|
else if (HitObject is IHasXPosition)
|
||||||
@ -255,22 +254,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the sample info list at a point in time.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
|
||||||
{
|
|
||||||
if (!(HitObject is IHasCurve curveData))
|
|
||||||
return HitObject.Samples;
|
|
||||||
|
|
||||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
|
||||||
|
|
||||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
|
||||||
return curveData.NodeSamples[index];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,7 +474,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
||||||
{
|
{
|
||||||
if (!(HitObject is IHasCurve curveData))
|
if (!(HitObject is IHasPathWithRepeats curveData))
|
||||||
return HitObject.Samples;
|
return HitObject.Samples;
|
||||||
|
|
||||||
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
|
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
|
||||||
@ -505,16 +505,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var holdNote = new HoldNote
|
newObject = new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = startTime,
|
StartTime = startTime,
|
||||||
Column = column,
|
|
||||||
Duration = endTime - startTime,
|
Duration = endTime - startTime,
|
||||||
Head = { Samples = sampleInfoListAt(startTime) },
|
Column = column,
|
||||||
Tail = { Samples = sampleInfoListAt(endTime) }
|
Samples = HitObject.Samples,
|
||||||
|
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
|
||||||
};
|
};
|
||||||
|
|
||||||
newObject = holdNote;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern.Add(newObject);
|
pattern.Add(newObject);
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap)
|
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap)
|
||||||
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
|
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
|
||||||
{
|
{
|
||||||
endTime = (HitObject as IHasEndTime)?.EndTime ?? 0;
|
endTime = (HitObject as IHasDuration)?.EndTime ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<Pattern> Generate()
|
public override IEnumerable<Pattern> Generate()
|
||||||
@ -64,21 +64,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (holdNote)
|
if (holdNote)
|
||||||
{
|
{
|
||||||
var hold = new HoldNote
|
newObject = new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
|
Duration = endTime - HitObject.StartTime,
|
||||||
Column = column,
|
Column = column,
|
||||||
Duration = endTime - HitObject.StartTime
|
Samples = HitObject.Samples,
|
||||||
|
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hold.Head.Samples == null)
|
|
||||||
hold.Head.Samples = new List<HitSampleInfo>();
|
|
||||||
|
|
||||||
hold.Head.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL });
|
|
||||||
|
|
||||||
hold.Tail.Samples = HitObject.Samples;
|
|
||||||
|
|
||||||
newObject = hold;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -37,12 +38,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
{
|
{
|
||||||
mods = Score.Mods;
|
mods = Score.Mods;
|
||||||
scaledScore = Score.TotalScore;
|
scaledScore = Score.TotalScore;
|
||||||
countPerfect = Score.Statistics[HitResult.Perfect];
|
countPerfect = Score.Statistics.GetOrDefault(HitResult.Perfect);
|
||||||
countGreat = Score.Statistics[HitResult.Great];
|
countGreat = Score.Statistics.GetOrDefault(HitResult.Great);
|
||||||
countGood = Score.Statistics[HitResult.Good];
|
countGood = Score.Statistics.GetOrDefault(HitResult.Good);
|
||||||
countOk = Score.Statistics[HitResult.Ok];
|
countOk = Score.Statistics.GetOrDefault(HitResult.Ok);
|
||||||
countMeh = Score.Statistics[HitResult.Meh];
|
countMeh = Score.Statistics.GetOrDefault(HitResult.Meh);
|
||||||
countMiss = Score.Statistics[HitResult.Miss];
|
countMiss = Score.Statistics.GetOrDefault(HitResult.Miss);
|
||||||
|
|
||||||
if (mods.Any(m => !m.Ranked))
|
if (mods.Any(m => !m.Ranked))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2,10 +2,15 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
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;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||||
{
|
{
|
||||||
@ -15,6 +20,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
private readonly EditNotePiece headPiece;
|
private readonly EditNotePiece headPiece;
|
||||||
private readonly EditNotePiece tailPiece;
|
private readonly EditNotePiece tailPiece;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IScrollingInfo scrollingInfo { get; set; }
|
||||||
|
|
||||||
public HoldNotePlacementBlueprint()
|
public HoldNotePlacementBlueprint()
|
||||||
: base(new HoldNote())
|
: base(new HoldNote())
|
||||||
{
|
{
|
||||||
@ -34,8 +42,21 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
|
|
||||||
if (Column != null)
|
if (Column != null)
|
||||||
{
|
{
|
||||||
headPiece.Y = PositionAt(HitObject.StartTime);
|
headPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.StartTime)).Y;
|
||||||
tailPiece.Y = PositionAt(HitObject.EndTime);
|
tailPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.EndTime)).Y;
|
||||||
|
|
||||||
|
switch (scrollingInfo.Direction.Value)
|
||||||
|
{
|
||||||
|
case ScrollingDirection.Down:
|
||||||
|
headPiece.Y -= headPiece.DrawHeight / 2;
|
||||||
|
tailPiece.Y -= tailPiece.DrawHeight / 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ScrollingDirection.Up:
|
||||||
|
headPiece.Y += headPiece.DrawHeight / 2;
|
||||||
|
tailPiece.Y += tailPiece.DrawHeight / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y));
|
var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y));
|
||||||
@ -46,25 +67,39 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
bodyPiece.Height = (bottomPosition - topPosition).Y;
|
bodyPiece.Height = (bottomPosition - topPosition).Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseUp(MouseUpEvent e)
|
||||||
|
{
|
||||||
|
if (e.Button != MouseButton.Left)
|
||||||
|
return;
|
||||||
|
|
||||||
|
base.OnMouseUp(e);
|
||||||
|
EndPlacement(true);
|
||||||
|
}
|
||||||
|
|
||||||
private double originalStartTime;
|
private double originalStartTime;
|
||||||
|
|
||||||
public override void UpdatePosition(Vector2 screenSpacePosition)
|
public override void UpdatePosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(screenSpacePosition);
|
base.UpdatePosition(result);
|
||||||
|
|
||||||
if (PlacementActive)
|
if (PlacementActive)
|
||||||
{
|
{
|
||||||
var endTime = TimeAt(screenSpacePosition);
|
if (result.Time is double endTime)
|
||||||
|
{
|
||||||
HitObject.StartTime = endTime < originalStartTime ? endTime : originalStartTime;
|
HitObject.StartTime = endTime < originalStartTime ? endTime : originalStartTime;
|
||||||
HitObject.Duration = Math.Abs(endTime - originalStartTime);
|
HitObject.Duration = Math.Abs(endTime - originalStartTime);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
headPiece.Width = tailPiece.Width = SnappedWidth;
|
if (result.Playfield != null)
|
||||||
headPiece.X = tailPiece.X = SnappedMousePosition.X;
|
{
|
||||||
|
headPiece.Width = tailPiece.Width = result.Playfield.DrawWidth;
|
||||||
|
headPiece.X = tailPiece.X = ToLocalSpace(result.ScreenSpacePosition).X;
|
||||||
|
}
|
||||||
|
|
||||||
originalStartTime = HitObject.StartTime = TimeAt(screenSpacePosition);
|
if (result.Time is double startTime)
|
||||||
|
originalStartTime = HitObject.StartTime = startTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,5 +76,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override Quad SelectionQuad => ScreenSpaceDrawQuad;
|
public override Quad SelectionQuad => ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
|
public override Vector2 ScreenSpaceSelectionPoint => DrawableObject.Head.ScreenSpaceDrawQuad.Centre;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,42 +1,34 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osuTK.Input;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||||
{
|
{
|
||||||
public abstract class ManiaPlacementBlueprint<T> : PlacementBlueprint,
|
public abstract class ManiaPlacementBlueprint<T> : PlacementBlueprint
|
||||||
IRequireHighFrequencyMousePosition // the playfield could be moving behind us
|
|
||||||
where T : ManiaHitObject
|
where T : ManiaHitObject
|
||||||
{
|
{
|
||||||
protected new T HitObject => (T)base.HitObject;
|
protected new T HitObject => (T)base.HitObject;
|
||||||
|
|
||||||
protected Column Column;
|
private Column column;
|
||||||
|
|
||||||
/// <summary>
|
public Column Column
|
||||||
/// The current mouse position, snapped to the closest column.
|
{
|
||||||
/// </summary>
|
get => column;
|
||||||
protected Vector2 SnappedMousePosition { get; private set; }
|
set
|
||||||
|
{
|
||||||
|
if (value == column)
|
||||||
|
return;
|
||||||
|
|
||||||
/// <summary>
|
column = value;
|
||||||
/// The width of the closest column to the current mouse position.
|
HitObject.Column = column.Index;
|
||||||
/// </summary>
|
}
|
||||||
protected float SnappedWidth { get; private set; }
|
}
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IManiaHitObjectComposer composer { get; set; }
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IScrollingInfo scrollingInfo { get; set; }
|
|
||||||
|
|
||||||
protected ManiaPlacementBlueprint(T hitObject)
|
protected ManiaPlacementBlueprint(T hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
@ -46,112 +38,22 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
if (Column == null)
|
if (e.Button != MouseButton.Left)
|
||||||
return base.OnMouseDown(e);
|
return false;
|
||||||
|
|
||||||
HitObject.Column = Column.Index;
|
if (Column == null)
|
||||||
BeginPlacement(TimeAt(e.ScreenSpaceMousePosition));
|
return false;
|
||||||
|
|
||||||
|
BeginPlacement(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnMouseUp(MouseUpEvent e)
|
public override void UpdatePosition(SnapResult result)
|
||||||
{
|
{
|
||||||
EndPlacement(true);
|
base.UpdatePosition(result);
|
||||||
base.OnMouseUp(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdatePosition(Vector2 screenSpacePosition)
|
|
||||||
{
|
|
||||||
if (!PlacementActive)
|
if (!PlacementActive)
|
||||||
Column = ColumnAt(screenSpacePosition);
|
Column = result.Playfield as Column;
|
||||||
|
|
||||||
if (Column == null) return;
|
|
||||||
|
|
||||||
SnappedWidth = Column.DrawWidth;
|
|
||||||
|
|
||||||
// Snap to the column
|
|
||||||
var parentPos = Parent.ToLocalSpace(Column.ToScreenSpace(new Vector2(Column.DrawWidth / 2, 0)));
|
|
||||||
SnappedMousePosition = new Vector2(parentPos.X, Parent.ToLocalSpace(screenSpacePosition).Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected double TimeAt(Vector2 screenSpacePosition)
|
|
||||||
{
|
|
||||||
if (Column == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var hitObjectContainer = Column.HitObjectContainer;
|
|
||||||
|
|
||||||
// If we're scrolling downwards, a position of 0 is actually further away from the hit target
|
|
||||||
// so we need to flip the vertical coordinate in the hitobject container's space
|
|
||||||
var hitObjectPos = mouseToHitObjectPosition(Column.HitObjectContainer.ToLocalSpace(screenSpacePosition)).Y;
|
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
hitObjectPos = hitObjectContainer.DrawHeight - hitObjectPos;
|
|
||||||
|
|
||||||
return scrollingInfo.Algorithm.TimeAt(hitObjectPos,
|
|
||||||
EditorClock.CurrentTime,
|
|
||||||
scrollingInfo.TimeRange.Value,
|
|
||||||
hitObjectContainer.DrawHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected float PositionAt(double time)
|
|
||||||
{
|
|
||||||
var pos = scrollingInfo.Algorithm.PositionAt(time,
|
|
||||||
EditorClock.CurrentTime,
|
|
||||||
scrollingInfo.TimeRange.Value,
|
|
||||||
Column.HitObjectContainer.DrawHeight);
|
|
||||||
|
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
pos = Column.HitObjectContainer.DrawHeight - pos;
|
|
||||||
|
|
||||||
return hitObjectToMousePosition(Column.HitObjectContainer.ToSpaceOfOtherDrawable(new Vector2(0, pos), Parent)).Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Column ColumnAt(Vector2 screenSpacePosition)
|
|
||||||
=> composer.ColumnAt(screenSpacePosition);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a mouse position to a hitobject position.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="mousePosition">The mouse position.</param>
|
|
||||||
/// <returns>The resulting hitobject position, acnhored at the top or bottom of the blueprint depending on the scroll direction.</returns>
|
|
||||||
private Vector2 mouseToHitObjectPosition(Vector2 mousePosition)
|
|
||||||
{
|
|
||||||
switch (scrollingInfo.Direction.Value)
|
|
||||||
{
|
|
||||||
case ScrollingDirection.Up:
|
|
||||||
mousePosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ScrollingDirection.Down:
|
|
||||||
mousePosition.Y += DefaultNotePiece.NOTE_HEIGHT / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mousePosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a hitobject position to a mouse position.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hitObjectPosition">The hitobject position.</param>
|
|
||||||
/// <returns>The resulting mouse position, anchored at the centre of the hitobject.</returns>
|
|
||||||
private Vector2 hitObjectToMousePosition(Vector2 hitObjectPosition)
|
|
||||||
{
|
|
||||||
switch (scrollingInfo.Direction.Value)
|
|
||||||
{
|
|
||||||
case ScrollingDirection.Up:
|
|
||||||
hitObjectPosition.Y += DefaultNotePiece.NOTE_HEIGHT / 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ScrollingDirection.Down:
|
|
||||||
hitObjectPosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hitObjectPosition;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -13,33 +11,19 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||||
{
|
{
|
||||||
public class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
||||||
{
|
{
|
||||||
public Vector2 ScreenSpaceDragPosition { get; private set; }
|
|
||||||
public Vector2 DragPosition { get; private set; }
|
|
||||||
|
|
||||||
public new DrawableManiaHitObject DrawableObject => (DrawableManiaHitObject)base.DrawableObject;
|
public new DrawableManiaHitObject DrawableObject => (DrawableManiaHitObject)base.DrawableObject;
|
||||||
|
|
||||||
protected IClock EditorClock { get; private set; }
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IScrollingInfo scrollingInfo { get; set; }
|
private IScrollingInfo scrollingInfo { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
|
||||||
private IManiaHitObjectComposer composer { get; set; }
|
|
||||||
|
|
||||||
public ManiaSelectionBlueprint(DrawableHitObject drawableObject)
|
|
||||||
: base(drawableObject)
|
: base(drawableObject)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.None;
|
RelativeSizeAxes = Axes.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(IAdjustableClock clock)
|
|
||||||
{
|
|
||||||
EditorClock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -47,22 +31,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
Position = Parent.ToLocalSpace(DrawableObject.ToScreenSpace(Vector2.Zero));
|
Position = Parent.ToLocalSpace(DrawableObject.ToScreenSpace(Vector2.Zero));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
|
||||||
{
|
|
||||||
ScreenSpaceDragPosition = e.ScreenSpaceMousePosition;
|
|
||||||
DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition);
|
|
||||||
|
|
||||||
return base.OnMouseDown(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDrag(DragEvent e)
|
|
||||||
{
|
|
||||||
base.OnDrag(e);
|
|
||||||
|
|
||||||
ScreenSpaceDragPosition = e.ScreenSpaceMousePosition;
|
|
||||||
DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Show()
|
public override void Show()
|
||||||
{
|
{
|
||||||
DrawableObject.AlwaysAlive = true;
|
DrawableObject.AlwaysAlive = true;
|
||||||
|
@ -2,29 +2,48 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
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 osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||||
{
|
{
|
||||||
public class NotePlacementBlueprint : ManiaPlacementBlueprint<Note>
|
public class NotePlacementBlueprint : ManiaPlacementBlueprint<Note>
|
||||||
{
|
{
|
||||||
|
private readonly EditNotePiece piece;
|
||||||
|
|
||||||
public NotePlacementBlueprint()
|
public NotePlacementBlueprint()
|
||||||
: base(new Note())
|
: base(new Note())
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
AutoSizeAxes = Axes.Y;
|
InternalChild = piece = new EditNotePiece { Origin = Anchor.Centre };
|
||||||
|
|
||||||
InternalChild = new EditNotePiece { RelativeSizeAxes = Axes.X };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
public override void UpdatePosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.Update();
|
base.UpdatePosition(result);
|
||||||
|
|
||||||
Width = SnappedWidth;
|
if (result.Playfield != null)
|
||||||
Position = SnappedMousePosition;
|
{
|
||||||
|
piece.Width = result.Playfield.DrawWidth;
|
||||||
|
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
if (e.Button != MouseButton.Left)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
base.OnMouseDown(e);
|
||||||
|
|
||||||
|
// Place the note immediately.
|
||||||
|
EndPlacement(true);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +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.Game.Rulesets.Mania.UI;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit
|
|
||||||
{
|
|
||||||
public interface IManiaHitObjectComposer
|
|
||||||
{
|
|
||||||
Column ColumnAt(Vector2 screenSpacePosition);
|
|
||||||
|
|
||||||
int TotalColumns { get; }
|
|
||||||
}
|
|
||||||
}
|
|
215
osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs
Normal file
215
osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Caching;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Edit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A grid which displays coloured beat divisor lines in proximity to the selection or placement cursor.
|
||||||
|
/// </summary>
|
||||||
|
public class ManiaBeatSnapGrid : Component
|
||||||
|
{
|
||||||
|
private const double visible_range = 750;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The range of time values of the current selection.
|
||||||
|
/// </summary>
|
||||||
|
public (double start, double end)? SelectionTimeRange
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == selectionTimeRange)
|
||||||
|
return;
|
||||||
|
|
||||||
|
selectionTimeRange = value;
|
||||||
|
lineCache.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private EditorBeatmap beatmap { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IScrollingInfo scrollingInfo { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private Bindable<WorkingBeatmap> working { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private BindableBeatDivisor beatDivisor { get; set; }
|
||||||
|
|
||||||
|
private readonly List<ScrollingHitObjectContainer> grids = new List<ScrollingHitObjectContainer>();
|
||||||
|
|
||||||
|
private readonly Cached lineCache = new Cached();
|
||||||
|
|
||||||
|
private (double start, double end)? selectionTimeRange;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(HitObjectComposer composer)
|
||||||
|
{
|
||||||
|
foreach (var stage in ((ManiaPlayfield)composer.Playfield).Stages)
|
||||||
|
{
|
||||||
|
foreach (var column in stage.Columns)
|
||||||
|
{
|
||||||
|
var lineContainer = new ScrollingHitObjectContainer();
|
||||||
|
|
||||||
|
grids.Add(lineContainer);
|
||||||
|
column.UnderlayElements.Add(lineContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beatDivisor.BindValueChanged(_ => createLines(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (!lineCache.IsValid)
|
||||||
|
{
|
||||||
|
lineCache.Validate();
|
||||||
|
createLines();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Stack<DrawableGridLine> availableLines = new Stack<DrawableGridLine>();
|
||||||
|
|
||||||
|
private void createLines()
|
||||||
|
{
|
||||||
|
foreach (var grid in grids)
|
||||||
|
{
|
||||||
|
foreach (var line in grid.Objects.OfType<DrawableGridLine>())
|
||||||
|
availableLines.Push(line);
|
||||||
|
|
||||||
|
grid.Clear(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectionTimeRange == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var range = selectionTimeRange.Value;
|
||||||
|
|
||||||
|
var timingPoint = beatmap.ControlPointInfo.TimingPointAt(range.start - visible_range);
|
||||||
|
|
||||||
|
double time = timingPoint.Time;
|
||||||
|
int beat = 0;
|
||||||
|
|
||||||
|
// progress time until in the visible range.
|
||||||
|
while (time < range.start - visible_range)
|
||||||
|
{
|
||||||
|
time += timingPoint.BeatLength / beatDivisor.Value;
|
||||||
|
beat++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (time < range.end + visible_range)
|
||||||
|
{
|
||||||
|
var nextTimingPoint = beatmap.ControlPointInfo.TimingPointAt(time);
|
||||||
|
|
||||||
|
// switch to the next timing point if we have reached it.
|
||||||
|
if (nextTimingPoint.Time > timingPoint.Time)
|
||||||
|
{
|
||||||
|
beat = 0;
|
||||||
|
time = nextTimingPoint.Time;
|
||||||
|
timingPoint = nextTimingPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color4 colour = BindableBeatDivisor.GetColourFor(
|
||||||
|
BindableBeatDivisor.GetDivisorForBeatIndex(Math.Max(1, beat), beatDivisor.Value), colours);
|
||||||
|
|
||||||
|
foreach (var grid in grids)
|
||||||
|
{
|
||||||
|
if (!availableLines.TryPop(out var line))
|
||||||
|
line = new DrawableGridLine();
|
||||||
|
|
||||||
|
line.HitObject.StartTime = time;
|
||||||
|
line.Colour = colour;
|
||||||
|
|
||||||
|
grid.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
beat++;
|
||||||
|
time += timingPoint.BeatLength / beatDivisor.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var grid in grids)
|
||||||
|
{
|
||||||
|
// required to update ScrollingHitObjectContainer's cache.
|
||||||
|
grid.UpdateSubTree();
|
||||||
|
|
||||||
|
foreach (var line in grid.Objects.OfType<DrawableGridLine>())
|
||||||
|
{
|
||||||
|
time = line.HitObject.StartTime;
|
||||||
|
|
||||||
|
if (time >= range.start && time <= range.end)
|
||||||
|
line.Alpha = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double timeSeparation = time < range.start ? range.start - time : time - range.end;
|
||||||
|
line.Alpha = (float)Math.Max(0, 1 - timeSeparation / visible_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DrawableGridLine : DrawableHitObject
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private IScrollingInfo scrollingInfo { get; set; }
|
||||||
|
|
||||||
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
public DrawableGridLine()
|
||||||
|
: base(new HitObject())
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Height = 2;
|
||||||
|
|
||||||
|
AddInternal(new Box { RelativeSizeAxes = Axes.Both });
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
direction.BindValueChanged(onDirectionChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
|
{
|
||||||
|
Origin = Anchor = direction.NewValue == ScrollingDirection.Up
|
||||||
|
? Anchor.TopLeft
|
||||||
|
: Anchor.BottomLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateInitialTransforms()
|
||||||
|
{
|
||||||
|
// don't perform any fading – we are handling that ourselves.
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
|
{
|
||||||
|
LifetimeEnd = HitObject.StartTime + visible_range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,5 +30,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
|
|
||||||
return base.CreateBlueprintFor(hitObject);
|
return base.CreateBlueprintFor(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,38 +6,78 @@ using osu.Game.Rulesets.Edit;
|
|||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit
|
namespace osu.Game.Rulesets.Mania.Edit
|
||||||
{
|
{
|
||||||
[Cached(Type = typeof(IManiaHitObjectComposer))]
|
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>
|
||||||
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>, IManiaHitObjectComposer
|
|
||||||
{
|
{
|
||||||
private DrawableManiaEditRuleset drawableRuleset;
|
private DrawableManiaEditRuleset drawableRuleset;
|
||||||
|
private ManiaBeatSnapGrid beatSnapGrid;
|
||||||
|
private InputManager inputManager;
|
||||||
|
|
||||||
public ManiaHitObjectComposer(Ruleset ruleset)
|
public ManiaHitObjectComposer(Ruleset ruleset)
|
||||||
: base(ruleset)
|
: base(ruleset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
[BackgroundDependencyLoader]
|
||||||
/// Retrieves the column that intersects a screen-space position.
|
private void load()
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="screenSpacePosition">The screen-space position.</param>
|
AddInternal(beatSnapGrid = new ManiaBeatSnapGrid());
|
||||||
/// <returns>The column which intersects with <paramref name="screenSpacePosition"/>.</returns>
|
}
|
||||||
public Column ColumnAt(Vector2 screenSpacePosition) => drawableRuleset.GetColumnByPosition(screenSpacePosition);
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
inputManager = GetContainingInputManager();
|
||||||
|
}
|
||||||
|
|
||||||
private DependencyContainer dependencies;
|
private DependencyContainer dependencies;
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns;
|
public new ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield);
|
||||||
|
|
||||||
|
public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo;
|
||||||
|
|
||||||
|
protected override Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) =>
|
||||||
|
Playfield.GetColumnByPosition(screenSpacePosition);
|
||||||
|
|
||||||
|
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||||
|
{
|
||||||
|
var result = base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
||||||
|
|
||||||
|
switch (ScrollingInfo.Direction.Value)
|
||||||
|
{
|
||||||
|
case ScrollingDirection.Down:
|
||||||
|
result.ScreenSpacePosition -= new Vector2(0, getNoteHeight() / 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ScrollingDirection.Up:
|
||||||
|
result.ScreenSpacePosition += new Vector2(0, getNoteHeight() / 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getNoteHeight() =>
|
||||||
|
Playfield.GetColumn(0).ToScreenSpace(new Vector2(DefaultNotePiece.NOTE_HEIGHT)).Y -
|
||||||
|
Playfield.GetColumn(0).ToScreenSpace(Vector2.Zero).Y;
|
||||||
|
|
||||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
{
|
{
|
||||||
@ -49,12 +89,36 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
return drawableRuleset;
|
return drawableRuleset;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ManiaBlueprintContainer(drawableRuleset.Playfield.AllHitObjects);
|
protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable<DrawableHitObject> hitObjects)
|
||||||
|
=> new ManiaBlueprintContainer(hitObjects);
|
||||||
|
|
||||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||||
{
|
{
|
||||||
new NoteCompositionTool(),
|
new NoteCompositionTool(),
|
||||||
new HoldNoteCompositionTool()
|
new HoldNoteCompositionTool()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
|
if (BlueprintContainer.CurrentTool is SelectTool)
|
||||||
|
{
|
||||||
|
if (EditorBeatmap.SelectedHitObjects.Any())
|
||||||
|
{
|
||||||
|
beatSnapGrid.SelectionTimeRange = (EditorBeatmap.SelectedHitObjects.Min(h => h.StartTime), EditorBeatmap.SelectedHitObjects.Max(h => h.GetEndTime()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
beatSnapGrid.SelectionTimeRange = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position);
|
||||||
|
if (result.Time is double time)
|
||||||
|
beatSnapGrid.SelectionTimeRange = (time, time);
|
||||||
|
else
|
||||||
|
beatSnapGrid.SelectionTimeRange = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
|
||||||
@ -20,90 +18,23 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
private IScrollingInfo scrollingInfo { get; set; }
|
private IScrollingInfo scrollingInfo { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IManiaHitObjectComposer composer { get; set; }
|
private HitObjectComposer composer { get; set; }
|
||||||
|
|
||||||
private IClock editorClock;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(IAdjustableClock clock)
|
|
||||||
{
|
|
||||||
editorClock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool HandleMovement(MoveSelectionEvent moveEvent)
|
public override bool HandleMovement(MoveSelectionEvent moveEvent)
|
||||||
{
|
{
|
||||||
var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint;
|
var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint;
|
||||||
int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column;
|
int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column;
|
||||||
|
|
||||||
adjustOrigins(maniaBlueprint);
|
|
||||||
performDragMovement(moveEvent);
|
|
||||||
performColumnMovement(lastColumn, moveEvent);
|
performColumnMovement(lastColumn, moveEvent);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ensures that the position of hitobjects remains centred to the mouse position.
|
|
||||||
/// E.g. The hitobject position will change if the editor scrolls while a hitobject is dragged.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reference">The <see cref="ManiaSelectionBlueprint"/> that received the drag event.</param>
|
|
||||||
private void adjustOrigins(ManiaSelectionBlueprint reference)
|
|
||||||
{
|
|
||||||
var referenceParent = (HitObjectContainer)reference.DrawableObject.Parent;
|
|
||||||
|
|
||||||
float offsetFromReferenceOrigin = reference.DragPosition.Y - reference.DrawableObject.OriginPosition.Y;
|
|
||||||
float targetPosition = referenceParent.ToLocalSpace(reference.ScreenSpaceDragPosition).Y - offsetFromReferenceOrigin;
|
|
||||||
|
|
||||||
// Flip the vertical coordinate space when scrolling downwards
|
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
targetPosition -= referenceParent.DrawHeight;
|
|
||||||
|
|
||||||
float movementDelta = targetPosition - reference.DrawableObject.Position.Y;
|
|
||||||
|
|
||||||
foreach (var b in SelectedBlueprints.OfType<ManiaSelectionBlueprint>())
|
|
||||||
b.DrawableObject.Y += movementDelta;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performDragMovement(MoveSelectionEvent moveEvent)
|
|
||||||
{
|
|
||||||
float delta = moveEvent.InstantDelta.Y;
|
|
||||||
|
|
||||||
// When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen.
|
|
||||||
// This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height.
|
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong
|
|
||||||
|
|
||||||
foreach (var selectionBlueprint in SelectedBlueprints)
|
|
||||||
{
|
|
||||||
var b = (OverlaySelectionBlueprint)selectionBlueprint;
|
|
||||||
|
|
||||||
var hitObject = b.DrawableObject;
|
|
||||||
var objectParent = (HitObjectContainer)hitObject.Parent;
|
|
||||||
|
|
||||||
// StartTime could be used to adjust the position if only one movement event was received per frame.
|
|
||||||
// However this is not the case and ScrollingHitObjectContainer performs movement in UpdateAfterChildren() so the position must also be updated to be valid for further movement events
|
|
||||||
hitObject.Y += delta;
|
|
||||||
|
|
||||||
float targetPosition = hitObject.Position.Y;
|
|
||||||
|
|
||||||
// The scrolling algorithm always assumes an anchor at the top of the screen, so the position must be flipped when scrolling downwards to reflect a top anchor
|
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
targetPosition = -targetPosition;
|
|
||||||
|
|
||||||
objectParent.Remove(hitObject);
|
|
||||||
|
|
||||||
hitObject.HitObject.StartTime = scrollingInfo.Algorithm.TimeAt(targetPosition,
|
|
||||||
editorClock.CurrentTime,
|
|
||||||
scrollingInfo.TimeRange.Value,
|
|
||||||
objectParent.DrawHeight);
|
|
||||||
|
|
||||||
objectParent.Add(hitObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
|
private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
|
||||||
{
|
{
|
||||||
var currentColumn = composer.ColumnAt(moveEvent.ScreenSpacePosition);
|
var maniaPlayfield = ((ManiaHitObjectComposer)composer).Playfield;
|
||||||
|
|
||||||
|
var currentColumn = maniaPlayfield.GetColumnByPosition(moveEvent.ScreenSpacePosition);
|
||||||
if (currentColumn == null)
|
if (currentColumn == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -122,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
maxColumn = obj.Column;
|
maxColumn = obj.Column;
|
||||||
}
|
}
|
||||||
|
|
||||||
columnDelta = Math.Clamp(columnDelta, -minColumn, composer.TotalColumns - 1 - maxColumn);
|
columnDelta = Math.Clamp(columnDelta, -minColumn, maniaPlayfield.TotalColumns - 1 - maxColumn);
|
||||||
|
|
||||||
foreach (var obj in SelectedHitObjects.OfType<ManiaHitObject>())
|
foreach (var obj in SelectedHitObjects.OfType<ManiaHitObject>())
|
||||||
obj.Column += columnDelta;
|
obj.Column += columnDelta;
|
||||||
|
@ -1,18 +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.Graphics;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Masks
|
|
||||||
{
|
|
||||||
public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
|
||||||
{
|
|
||||||
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
|
|
||||||
: base(drawableObject)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -51,7 +51,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
|
|
||||||
AddRangeInternal(new[]
|
AddRangeInternal(new[]
|
||||||
{
|
{
|
||||||
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece())
|
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
})
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X
|
RelativeSizeAxes = Axes.X
|
||||||
},
|
},
|
||||||
@ -127,6 +130,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void PlaySamples()
|
||||||
|
{
|
||||||
|
// Samples are played by the head/tail notes.
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
@ -13,11 +13,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public abstract class DrawableManiaHitObject : DrawableHitObject<ManiaHitObject>
|
public abstract class DrawableManiaHitObject : DrawableHitObject<ManiaHitObject>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Whether this <see cref="DrawableManiaHitObject"/> should always remain alive.
|
|
||||||
/// </summary>
|
|
||||||
internal bool AlwaysAlive;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="ManiaAction"/> which causes this <see cref="DrawableManiaHitObject{TObject}"/> to be hit.
|
/// The <see cref="ManiaAction"/> which causes this <see cref="DrawableManiaHitObject{TObject}"/> to be hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -54,7 +49,62 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
Direction.BindValueChanged(OnDirectionChanged, true);
|
Direction.BindValueChanged(OnDirectionChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ShouldBeAlive => AlwaysAlive || base.ShouldBeAlive;
|
private double computedLifetimeStart;
|
||||||
|
|
||||||
|
public override double LifetimeStart
|
||||||
|
{
|
||||||
|
get => base.LifetimeStart;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
computedLifetimeStart = value;
|
||||||
|
|
||||||
|
if (!AlwaysAlive)
|
||||||
|
base.LifetimeStart = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double computedLifetimeEnd;
|
||||||
|
|
||||||
|
public override double LifetimeEnd
|
||||||
|
{
|
||||||
|
get => base.LifetimeEnd;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
computedLifetimeEnd = value;
|
||||||
|
|
||||||
|
if (!AlwaysAlive)
|
||||||
|
base.LifetimeEnd = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool alwaysAlive;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this <see cref="DrawableManiaHitObject"/> should always remain alive.
|
||||||
|
/// </summary>
|
||||||
|
internal bool AlwaysAlive
|
||||||
|
{
|
||||||
|
get => alwaysAlive;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (alwaysAlive == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
alwaysAlive = value;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
// Set the base lifetimes directly, to avoid mangling the computed lifetimes
|
||||||
|
base.LifetimeStart = double.MinValue;
|
||||||
|
base.LifetimeEnd = double.MaxValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LifetimeStart = computedLifetimeStart;
|
||||||
|
LifetimeEnd = computedLifetimeEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
protected virtual void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
|||||||
|
|
||||||
public DefaultBodyPiece()
|
public DefaultBodyPiece()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
Blending = BlendingParameters.Additive;
|
Blending = BlendingParameters.Additive;
|
||||||
|
|
||||||
AddLayout(subtractionCache);
|
AddLayout(subtractionCache);
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
// 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.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -12,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a hit object which requires pressing, holding, and releasing a key.
|
/// Represents a hit object which requires pressing, holding, and releasing a key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class HoldNote : ManiaHitObject, IHasEndTime
|
public class HoldNote : ManiaHitObject, IHasDuration
|
||||||
{
|
{
|
||||||
public double EndTime
|
public double EndTime
|
||||||
{
|
{
|
||||||
@ -28,6 +31,8 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
duration = value;
|
duration = value;
|
||||||
|
|
||||||
|
if (Tail != null)
|
||||||
Tail.StartTime = EndTime;
|
Tail.StartTime = EndTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +43,11 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
base.StartTime = value;
|
base.StartTime = value;
|
||||||
|
|
||||||
|
if (Head != null)
|
||||||
Head.StartTime = value;
|
Head.StartTime = value;
|
||||||
|
|
||||||
|
if (Tail != null)
|
||||||
Tail.StartTime = EndTime;
|
Tail.StartTime = EndTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,20 +58,26 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
base.Column = value;
|
base.Column = value;
|
||||||
|
|
||||||
|
if (Head != null)
|
||||||
Head.Column = value;
|
Head.Column = value;
|
||||||
|
|
||||||
|
if (Tail != null)
|
||||||
Tail.Column = value;
|
Tail.Column = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<IList<HitSampleInfo>> NodeSamples { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The head note of the hold.
|
/// The head note of the hold.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Note Head = new Note();
|
public Note Head { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The tail note of the hold.
|
/// The tail note of the hold.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly TailNote Tail = new TailNote();
|
public TailNote Tail { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time between ticks of this hold.
|
/// The time between ticks of this hold.
|
||||||
@ -77,23 +92,36 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
|
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects()
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects(cancellationToken);
|
||||||
|
|
||||||
createTicks();
|
createTicks(cancellationToken);
|
||||||
|
|
||||||
AddNested(Head);
|
AddNested(Head = new Note
|
||||||
AddNested(Tail);
|
{
|
||||||
|
StartTime = StartTime,
|
||||||
|
Column = Column,
|
||||||
|
Samples = getNodeSamples(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
AddNested(Tail = new TailNote
|
||||||
|
{
|
||||||
|
StartTime = EndTime,
|
||||||
|
Column = Column,
|
||||||
|
Samples = getNodeSamples((NodeSamples?.Count - 1) ?? 1),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTicks()
|
private void createTicks(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (tickSpacing == 0)
|
if (tickSpacing == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing)
|
for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
AddNested(new HoldNoteTick
|
AddNested(new HoldNoteTick
|
||||||
{
|
{
|
||||||
StartTime = t,
|
StartTime = t,
|
||||||
@ -105,5 +133,8 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
|
|
||||||
|
private IList<HitSampleInfo> getNodeSamples(int nodeIndex) =>
|
||||||
|
nodeIndex < NodeSamples?.Count ? NodeSamples[nodeIndex] : Samples;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,12 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Game.Rulesets.Mania.Objects.Types;
|
using osu.Game.Rulesets.Mania.Objects.Types;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects
|
namespace osu.Game.Rulesets.Mania.Objects
|
||||||
{
|
{
|
||||||
public abstract class ManiaHitObject : HitObject, IHasColumn
|
public abstract class ManiaHitObject : HitObject, IHasColumn, IHasXPosition
|
||||||
{
|
{
|
||||||
public readonly Bindable<int> ColumnBindable = new Bindable<int>();
|
public readonly Bindable<int> ColumnBindable = new Bindable<int>();
|
||||||
|
|
||||||
@ -20,5 +21,11 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
||||||
|
|
||||||
|
#region LegacyBeatmapEncoder
|
||||||
|
|
||||||
|
float IHasXPosition.X => Column;
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
|||||||
{
|
{
|
||||||
public class ManiaHitWindows : HitWindows
|
public class ManiaHitWindows : HitWindows
|
||||||
{
|
{
|
||||||
|
public override bool IsHitResultAllowed(HitResult result)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case HitResult.Perfect:
|
||||||
|
case HitResult.Great:
|
||||||
|
case HitResult.Good:
|
||||||
|
case HitResult.Ok:
|
||||||
|
case HitResult.Meh:
|
||||||
|
case HitResult.Miss:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,10 +52,10 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if (leftSprite?.Height > 0)
|
if (leftSprite?.Height > 0)
|
||||||
leftSprite.Scale = new Vector2(DrawHeight / leftSprite.Height);
|
leftSprite.Scale = new Vector2(1, DrawHeight / leftSprite.Height);
|
||||||
|
|
||||||
if (rightSprite?.Height > 0)
|
if (rightSprite?.Height > 0)
|
||||||
rightSprite.Scale = new Vector2(DrawHeight / rightSprite.Height);
|
rightSprite.Scale = new Vector2(1, DrawHeight / rightSprite.Height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
internal readonly Container TopLevelContainer;
|
internal readonly Container TopLevelContainer;
|
||||||
|
|
||||||
|
public Container UnderlayElements => hitObjectArea.UnderlayElements;
|
||||||
|
|
||||||
public Column(int index)
|
public Column(int index)
|
||||||
{
|
{
|
||||||
Index = index;
|
Index = index;
|
||||||
|
@ -12,6 +12,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
public class ColumnHitObjectArea : HitObjectArea
|
public class ColumnHitObjectArea : HitObjectArea
|
||||||
{
|
{
|
||||||
public readonly Container Explosions;
|
public readonly Container Explosions;
|
||||||
|
|
||||||
|
public readonly Container UnderlayElements;
|
||||||
|
|
||||||
private readonly Drawable hitTarget;
|
private readonly Drawable hitTarget;
|
||||||
|
|
||||||
public ColumnHitObjectArea(int columnIndex, HitObjectContainer hitObjectContainer)
|
public ColumnHitObjectArea(int columnIndex, HitObjectContainer hitObjectContainer)
|
||||||
@ -19,6 +22,11 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
{
|
{
|
||||||
AddRangeInternal(new[]
|
AddRangeInternal(new[]
|
||||||
{
|
{
|
||||||
|
UnderlayElements = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Depth = 2,
|
||||||
|
},
|
||||||
hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget, columnIndex), _ => new DefaultHitTarget())
|
hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget, columnIndex), _ => new DefaultHitTarget())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
// 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.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -21,7 +23,6 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
@ -48,6 +49,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||||
|
private readonly Bindable<double> configTimeRange = new BindableDouble();
|
||||||
|
|
||||||
|
// Stores the current speed adjustment active in gameplay.
|
||||||
|
private readonly Track speedAdjustmentTrack = new TrackVirtual(0);
|
||||||
|
|
||||||
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
@ -58,6 +63,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
foreach (var mod in Mods.OfType<IApplicableToTrack>())
|
||||||
|
mod.ApplyToTrack(speedAdjustmentTrack);
|
||||||
|
|
||||||
bool isForCurrentRuleset = Beatmap.BeatmapInfo.Ruleset.Equals(Ruleset.RulesetInfo);
|
bool isForCurrentRuleset = Beatmap.BeatmapInfo.Ruleset.Equals(Ruleset.RulesetInfo);
|
||||||
|
|
||||||
foreach (var p in ControlPoints)
|
foreach (var p in ControlPoints)
|
||||||
@ -76,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
|
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
|
||||||
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
|
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
|
||||||
|
|
||||||
Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange);
|
Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AdjustScrollSpeed(int amount)
|
protected override void AdjustScrollSpeed(int amount)
|
||||||
@ -86,16 +94,18 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
private double relativeTimeRange
|
private double relativeTimeRange
|
||||||
{
|
{
|
||||||
get => MAX_TIME_RANGE / TimeRange.Value;
|
get => MAX_TIME_RANGE / configTimeRange.Value;
|
||||||
set => TimeRange.Value = MAX_TIME_RANGE / value;
|
set => configTimeRange.Value = MAX_TIME_RANGE / value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
protected override void Update()
|
||||||
/// Retrieves the column that intersects a screen-space position.
|
{
|
||||||
/// </summary>
|
base.Update();
|
||||||
/// <param name="screenSpacePosition">The screen-space position.</param>
|
|
||||||
/// <returns>The column which intersects with <paramref name="screenSpacePosition"/>.</returns>
|
updateTimeRange();
|
||||||
public Column GetColumnByPosition(Vector2 screenSpacePosition) => Playfield.GetColumnByPosition(screenSpacePosition);
|
}
|
||||||
|
|
||||||
|
private void updateTimeRange() => TimeRange.Value = configTimeRange.Value * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value;
|
||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer();
|
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
[Cached]
|
[Cached]
|
||||||
public class ManiaPlayfield : ScrollingPlayfield
|
public class ManiaPlayfield : ScrollingPlayfield
|
||||||
{
|
{
|
||||||
|
public IReadOnlyList<Stage> Stages => stages;
|
||||||
|
|
||||||
private readonly List<Stage> stages = new List<Stage>();
|
private readonly List<Stage> stages = new List<Stage>();
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => stages.Any(s => s.ReceivePositionalInputAt(screenSpacePos));
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => stages.Any(s => s.ReceivePositionalInputAt(screenSpacePos));
|
||||||
@ -73,7 +75,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
foreach (var column in stage.Columns)
|
foreach (var column in stage.Columns)
|
||||||
{
|
{
|
||||||
if (column.ReceivePositionalInputAt(screenSpacePosition))
|
if (column.ReceivePositionalInputAt(new Vector2(screenSpacePosition.X, column.ScreenSpaceDrawQuad.Centre.Y)))
|
||||||
{
|
{
|
||||||
found = column;
|
found = column;
|
||||||
break;
|
break;
|
||||||
@ -87,6 +89,31 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a <see cref="Column"/> by index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The index of the column.</param>
|
||||||
|
/// <returns>The <see cref="Column"/> corresponding to the given index.</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="index"/> is less than 0 or greater than <see cref="TotalColumns"/>.</exception>
|
||||||
|
public Column GetColumn(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index > TotalColumns - 1)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
|
foreach (var stage in stages)
|
||||||
|
{
|
||||||
|
if (index >= stage.Columns.Count)
|
||||||
|
{
|
||||||
|
index -= stage.Columns.Count;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stage.Columns[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the total amount of columns across all stages in this playfield.
|
/// Retrieves the total amount of columns across all stages in this playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,21 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
public abstract class OsuSkinnableTestScene : SkinnableTestScene
|
public abstract class OsuSkinnableTestScene : SkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(OsuRuleset),
|
|
||||||
typeof(OsuLegacySkinTransformer),
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -15,12 +14,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public class TestSceneDrawableJudgement : OsuSkinnableTestScene
|
public class TestSceneDrawableJudgement : OsuSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
|
||||||
{
|
|
||||||
typeof(DrawableJudgement),
|
|
||||||
typeof(DrawableOsuJudgement)
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
public TestSceneDrawableJudgement()
|
public TestSceneDrawableJudgement()
|
||||||
{
|
{
|
||||||
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
|
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
|
||||||
|
@ -2,16 +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 System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing.Input;
|
using osu.Framework.Testing.Input;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -20,16 +16,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneGameplayCursor : OsuSkinnableTestScene
|
public class TestSceneGameplayCursor : OsuSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
|
||||||
{
|
|
||||||
typeof(GameplayCursorContainer),
|
|
||||||
typeof(OsuCursorContainer),
|
|
||||||
typeof(OsuCursor),
|
|
||||||
typeof(LegacyCursor),
|
|
||||||
typeof(LegacyCursorTrail),
|
|
||||||
typeof(CursorTrail)
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private GameplayBeatmap gameplayBeatmap;
|
private GameplayBeatmap gameplayBeatmap;
|
||||||
|
|
||||||
|
@ -8,8 +8,6 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -20,11 +18,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneHitCircle : OsuSkinnableTestScene
|
public class TestSceneHitCircle : OsuSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(DrawableHitCircle)
|
|
||||||
};
|
|
||||||
|
|
||||||
private int depthIndex;
|
private int depthIndex;
|
||||||
|
|
||||||
public TestSceneHitCircle()
|
public TestSceneHitCircle()
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
|
||||||
@ -12,8 +9,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneHitCircleHidden : TestSceneHitCircle
|
public class TestSceneHitCircleHidden : TestSceneHitCircle
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
|
@ -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;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -16,7 +15,6 @@ using osu.Game.Rulesets.Osu.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Osu.Edit;
|
using osu.Game.Rulesets.Osu.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -28,18 +26,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
private const double beat_length = 100;
|
private const double beat_length = 100;
|
||||||
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
||||||
|
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(CircularDistanceSnapGrid)
|
|
||||||
};
|
|
||||||
|
|
||||||
[Cached(typeof(EditorBeatmap))]
|
[Cached(typeof(EditorBeatmap))]
|
||||||
private readonly EditorBeatmap editorBeatmap;
|
private readonly EditorBeatmap editorBeatmap;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||||
|
|
||||||
[Cached(typeof(IDistanceSnapProvider))]
|
[Cached(typeof(IPositionSnapProvider))]
|
||||||
private readonly SnapProvider snapProvider = new SnapProvider();
|
private readonly SnapProvider snapProvider = new SnapProvider();
|
||||||
|
|
||||||
private TestOsuDistanceSnapGrid grid;
|
private TestOsuDistanceSnapGrid grid;
|
||||||
@ -179,9 +172,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SnapProvider : IDistanceSnapProvider
|
private class SnapProvider : IPositionSnapProvider
|
||||||
{
|
{
|
||||||
public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => (position, time);
|
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
||||||
|
|
||||||
public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length;
|
public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length;
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
@ -11,11 +9,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneOsuPlayer : PlayerTestScene
|
public class TestSceneOsuPlayer : PlayerTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(OsuRuleset),
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,7 +399,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public TestSlider()
|
public TestSlider()
|
||||||
{
|
{
|
||||||
DefaultsApplied += () =>
|
DefaultsApplied += _ =>
|
||||||
{
|
{
|
||||||
HeadCircle.HitWindows = new TestHitWindows();
|
HeadCircle.HitWindows = new TestHitWindows();
|
||||||
TailCircle.HitWindows = new TestHitWindows();
|
TailCircle.HitWindows = new TestHitWindows();
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Humanizer;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -19,13 +16,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public class TestScenePathControlPointVisualiser : OsuTestScene
|
public class TestScenePathControlPointVisualiser : OsuTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(StringHumanizeExtensions),
|
|
||||||
typeof(PathControlPointPiece),
|
|
||||||
typeof(PathControlPointConnectionPiece)
|
|
||||||
};
|
|
||||||
|
|
||||||
private Slider slider;
|
private Slider slider;
|
||||||
private PathControlPointVisualiser visualiser;
|
private PathControlPointVisualiser visualiser;
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
@ -14,11 +12,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public class TestSceneResumeOverlay : OsuManualInputManagerTestScene
|
public class TestSceneResumeOverlay : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(OsuResumeOverlay),
|
|
||||||
};
|
|
||||||
|
|
||||||
public TestSceneResumeOverlay()
|
public TestSceneResumeOverlay()
|
||||||
{
|
{
|
||||||
ManualOsuInputManager osuInputManager;
|
ManualOsuInputManager osuInputManager;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -21,29 +20,12 @@ 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;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneSlider : OsuSkinnableTestScene
|
public class TestSceneSlider : OsuSkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(Slider),
|
|
||||||
typeof(SliderTick),
|
|
||||||
typeof(SliderTailCircle),
|
|
||||||
typeof(SliderBall),
|
|
||||||
typeof(SliderBody),
|
|
||||||
typeof(SnakingSliderBody),
|
|
||||||
typeof(DrawableSlider),
|
|
||||||
typeof(DrawableSliderTick),
|
|
||||||
typeof(DrawableSliderTail),
|
|
||||||
typeof(DrawableSliderHead),
|
|
||||||
typeof(DrawableSliderRepeat),
|
|
||||||
typeof(DrawableOsuHitObject)
|
|
||||||
};
|
|
||||||
|
|
||||||
private Container content;
|
private Container content;
|
||||||
|
|
||||||
protected override Container<Drawable> Content
|
protected override Container<Drawable> Content
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
|
||||||
@ -12,8 +9,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneSliderHidden : TestSceneSlider
|
public class TestSceneSliderHidden : TestSceneSlider
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -13,8 +12,6 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
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.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
|
||||||
using osu.Game.Rulesets.Osu.Replays;
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -27,17 +24,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public class TestSceneSliderInput : RateAdjustedBeatmapTestScene
|
public class TestSceneSliderInput : RateAdjustedBeatmapTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(SliderBall),
|
|
||||||
typeof(DrawableSlider),
|
|
||||||
typeof(DrawableSliderTick),
|
|
||||||
typeof(DrawableSliderRepeat),
|
|
||||||
typeof(DrawableOsuHitObject),
|
|
||||||
typeof(DrawableSliderHead),
|
|
||||||
typeof(DrawableSliderTail),
|
|
||||||
};
|
|
||||||
|
|
||||||
private const double time_before_slider = 250;
|
private const double time_before_slider = 250;
|
||||||
private const double time_slider_start = 1500;
|
private const double time_slider_start = 1500;
|
||||||
private const double time_during_slide_1 = 2500;
|
private const double time_during_slide_1 = 2500;
|
||||||
|
@ -259,6 +259,23 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
assertControlPointType(2, PathType.PerfectCurve);
|
assertControlPointType(2, PathType.PerfectCurve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeginPlacementWithoutReleasingMouse()
|
||||||
|
{
|
||||||
|
addMovementStep(new Vector2(200));
|
||||||
|
AddStep("press left button", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
|
||||||
|
addMovementStep(new Vector2(400, 200));
|
||||||
|
AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
|
||||||
|
addClickStep(MouseButton.Right);
|
||||||
|
|
||||||
|
assertPlaced(true);
|
||||||
|
assertLength(200);
|
||||||
|
assertControlPointCount(2);
|
||||||
|
assertControlPointType(0, PathType.Linear);
|
||||||
|
}
|
||||||
|
|
||||||
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));
|
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));
|
||||||
|
|
||||||
private void addClickStep(MouseButton button)
|
private void addClickStep(MouseButton button)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -22,16 +20,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public class TestSceneSliderSelectionBlueprint : SelectionBlueprintTestScene
|
public class TestSceneSliderSelectionBlueprint : SelectionBlueprintTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(SliderSelectionBlueprint),
|
|
||||||
typeof(SliderCircleSelectionBlueprint),
|
|
||||||
typeof(SliderBodyPiece),
|
|
||||||
typeof(SliderCircle),
|
|
||||||
typeof(PathControlPointVisualiser),
|
|
||||||
typeof(PathControlPointPiece)
|
|
||||||
};
|
|
||||||
|
|
||||||
private Slider slider;
|
private Slider slider;
|
||||||
private DrawableSlider drawableObject;
|
private DrawableSlider drawableObject;
|
||||||
private TestSliderBlueprint blueprint;
|
private TestSliderBlueprint blueprint;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -12,7 +10,6 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
@ -20,13 +17,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneSpinner : OsuTestScene
|
public class TestSceneSpinner : OsuTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(SpinnerDisc),
|
|
||||||
typeof(DrawableSpinner),
|
|
||||||
typeof(DrawableOsuHitObject)
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly Container content;
|
private readonly Container content;
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
|
||||||
@ -12,8 +9,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneSpinnerHidden : TestSceneSpinner
|
public class TestSceneSpinnerHidden : TestSceneSpinner
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
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;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -18,12 +15,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public class TestSceneSpinnerSelectionBlueprint : SelectionBlueprintTestScene
|
public class TestSceneSpinnerSelectionBlueprint : SelectionBlueprintTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(SpinnerSelectionBlueprint),
|
|
||||||
typeof(SpinnerPiece)
|
|
||||||
};
|
|
||||||
|
|
||||||
public TestSceneSpinnerSelectionBlueprint()
|
public TestSceneSpinnerSelectionBlueprint()
|
||||||
{
|
{
|
||||||
var spinner = new Spinner
|
var spinner = new Spinner
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -12,7 +10,6 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
@ -20,14 +17,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneSpinnerSpunOut : OsuTestScene
|
public class TestSceneSpinnerSpunOut : OsuTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(SpinnerDisc),
|
|
||||||
typeof(DrawableSpinner),
|
|
||||||
typeof(DrawableOsuHitObject),
|
|
||||||
typeof(OsuModSpunOut)
|
|
||||||
};
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
|
|
||||||
switch (original)
|
switch (original)
|
||||||
{
|
{
|
||||||
case IHasCurve curveData:
|
case IHasPathWithRepeats curveData:
|
||||||
return new Slider
|
return new Slider
|
||||||
{
|
{
|
||||||
StartTime = original.StartTime,
|
StartTime = original.StartTime,
|
||||||
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / beatmap.ControlPointInfo.DifficultyPointAt(original.StartTime).SpeedMultiplier : 1
|
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / beatmap.ControlPointInfo.DifficultyPointAt(original.StartTime).SpeedMultiplier : 1
|
||||||
}.Yield();
|
}.Yield();
|
||||||
|
|
||||||
case IHasEndTime endTimeData:
|
case IHasDuration endTimeData:
|
||||||
return new Spinner
|
return new Spinner
|
||||||
{
|
{
|
||||||
StartTime = original.StartTime,
|
StartTime = original.StartTime,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user