Merge branch 'master' into diffcalc/fix/clockrate-adjusted-decay
@ -194,4 +194,7 @@ dotnet_diagnostic.IDE0068.severity = none
|
|||||||
dotnet_diagnostic.IDE0069.severity = none
|
dotnet_diagnostic.IDE0069.severity = none
|
||||||
|
|
||||||
#Disable operator overloads requiring alternate named methods
|
#Disable operator overloads requiring alternate named methods
|
||||||
dotnet_diagnostic.CA2225.severity = none
|
dotnet_diagnostic.CA2225.severity = none
|
||||||
|
|
||||||
|
# Banned APIs
|
||||||
|
dotnet_diagnostic.RS0030.severity = error
|
16
.github/ISSUE_TEMPLATE/01-bug-issues.md
vendored
@ -1,7 +1,18 @@
|
|||||||
---
|
---
|
||||||
name: Bug Report
|
name: Bug Report
|
||||||
about: Issues regarding encountered bugs.
|
about: Report a bug or crash to desktop
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
IMPORTANT: Your issue may already be reported.
|
||||||
|
|
||||||
|
Please check:
|
||||||
|
- Pinned issues, at the top of https://github.com/ppy/osu/issues
|
||||||
|
- Current priority 0 issues at https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0
|
||||||
|
- Search for your issue. If you find that it already exists, please respond with a reaction or add any further information that may be helpful.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
**Describe the bug:**
|
**Describe the bug:**
|
||||||
|
|
||||||
**Screenshots or videos showing encountered issue:**
|
**Screenshots or videos showing encountered issue:**
|
||||||
@ -9,8 +20,11 @@ about: Issues regarding encountered bugs.
|
|||||||
**osu!lazer version:**
|
**osu!lazer version:**
|
||||||
|
|
||||||
**Logs:**
|
**Logs:**
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
*please attach logs here, which are located at:*
|
*please attach logs here, which are located at:*
|
||||||
- `%AppData%/osu/logs` *(on Windows),*
|
- `%AppData%/osu/logs` *(on Windows),*
|
||||||
- `~/.local/share/osu/logs` *(on Linux & macOS).*
|
- `~/.local/share/osu/logs` *(on Linux & macOS).*
|
||||||
|
- `Android/Data/sh.ppy.osulazer/logs` *(on Android)*,
|
||||||
|
- on iOS they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer)
|
||||||
-->
|
-->
|
||||||
|
18
.github/ISSUE_TEMPLATE/02-crash-issues.md
vendored
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
name: Crash Report
|
|
||||||
about: Issues regarding crashes or permanent freezes.
|
|
||||||
---
|
|
||||||
**Describe the crash:**
|
|
||||||
|
|
||||||
**Screenshots or videos showing encountered issue:**
|
|
||||||
|
|
||||||
**osu!lazer version:**
|
|
||||||
|
|
||||||
**Logs:**
|
|
||||||
<!--
|
|
||||||
*please attach logs here, which are located at:*
|
|
||||||
- `%AppData%/osu/logs` *(on Windows),*
|
|
||||||
- `~/.local/share/osu/logs` *(on Linux & macOS).*
|
|
||||||
-->
|
|
||||||
|
|
||||||
**Computer Specifications:**
|
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
about: Features you would like to see in the game!
|
about: Propose a feature you would like to see in the game!
|
||||||
---
|
---
|
||||||
**Describe the new feature:**
|
**Describe the new feature:**
|
||||||
|
|
@ -1,20 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="osu! (legacy osuTK)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
|
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net5.0/osu!.dll" />
|
|
||||||
<option name="PROGRAM_PARAMETERS" value="--tk" />
|
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/net5.0" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
|
||||||
<option name="USE_MONO" value="0" />
|
|
||||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
|
||||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Desktop/osu.Desktop.csproj" />
|
|
||||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
|
||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
|
||||||
<option name="PROJECT_TFM" value="net5.0" />
|
|
||||||
<method v="2">
|
|
||||||
<option name="Build" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
@ -7,3 +7,4 @@ M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
|||||||
M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900)
|
M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900)
|
||||||
T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods.
|
T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods.
|
||||||
T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods.
|
T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods.
|
||||||
|
M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast<T>() instead.
|
@ -30,7 +30,7 @@
|
|||||||
<Rule Id="CA1819" Action="None" />
|
<Rule Id="CA1819" Action="None" />
|
||||||
<Rule Id="CA1822" Action="None" />
|
<Rule Id="CA1822" Action="None" />
|
||||||
<Rule Id="CA1823" Action="None" />
|
<Rule Id="CA1823" Action="None" />
|
||||||
<Rule Id="CA2007" Action="None" />
|
<Rule Id="CA2007" Action="Warning" />
|
||||||
<Rule Id="CA2214" Action="None" />
|
<Rule Id="CA2214" Action="None" />
|
||||||
<Rule Id="CA2227" Action="None" />
|
<Rule Id="CA2227" Action="None" />
|
||||||
</Rules>
|
</Rules>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<ItemGroup Label="Code Analysis">
|
<ItemGroup Label="Code Analysis">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.2" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.2" PrivateAssets="All" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.2" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Code Analysis">
|
<PropertyGroup Label="Code Analysis">
|
||||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
||||||
|
@ -52,6 +52,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.219.1" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.323.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -17,7 +17,7 @@ using osu.Game.Database;
|
|||||||
|
|
||||||
namespace osu.Android
|
namespace osu.Android
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance)]
|
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed" })]
|
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed" })]
|
||||||
@ -100,15 +100,15 @@ namespace osu.Android
|
|||||||
// copy to an arbitrary-access memory stream to be able to proceed with the import.
|
// copy to an arbitrary-access memory stream to be able to proceed with the import.
|
||||||
var copy = new MemoryStream();
|
var copy = new MemoryStream();
|
||||||
using (var stream = ContentResolver.OpenInputStream(uri))
|
using (var stream = ContentResolver.OpenInputStream(uri))
|
||||||
await stream.CopyToAsync(copy);
|
await stream.CopyToAsync(copy).ConfigureAwait(false);
|
||||||
|
|
||||||
lock (tasks)
|
lock (tasks)
|
||||||
{
|
{
|
||||||
tasks.Add(new ImportTask(copy, filename));
|
tasks.Add(new ImportTask(copy, filename));
|
||||||
}
|
}
|
||||||
}));
|
})).ConfigureAwait(false);
|
||||||
|
|
||||||
await game.Import(tasks.ToArray());
|
await game.Import(tasks.ToArray()).ConfigureAwait(false);
|
||||||
}, TaskCreationOptions.LongRunning);
|
}, TaskCreationOptions.LongRunning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,24 +136,12 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||||
|
|
||||||
switch (host.Window)
|
var desktopWindow = (SDL2DesktopWindow)host.Window;
|
||||||
{
|
|
||||||
// Legacy osuTK DesktopGameWindow
|
|
||||||
case OsuTKDesktopWindow desktopGameWindow:
|
|
||||||
desktopGameWindow.CursorState |= CursorState.Hidden;
|
|
||||||
desktopGameWindow.SetIconFromStream(iconStream);
|
|
||||||
desktopGameWindow.Title = Name;
|
|
||||||
desktopGameWindow.FileDrop += (_, e) => fileDrop(e.FileNames);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// SDL2 DesktopWindow
|
desktopWindow.CursorState |= CursorState.Hidden;
|
||||||
case SDL2DesktopWindow desktopWindow:
|
desktopWindow.SetIconFromStream(iconStream);
|
||||||
desktopWindow.CursorState |= CursorState.Hidden;
|
desktopWindow.Title = Name;
|
||||||
desktopWindow.SetIconFromStream(iconStream);
|
desktopWindow.DragDrop += f => fileDrop(new[] { f });
|
||||||
desktopWindow.Title = Name;
|
|
||||||
desktopWindow.DragDrop += f => fileDrop(new[] { f });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fileDrop(string[] filePaths)
|
private void fileDrop(string[] filePaths)
|
||||||
|
@ -22,9 +22,8 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
// Back up the cwd before DesktopGameHost changes it
|
// Back up the cwd before DesktopGameHost changes it
|
||||||
var cwd = Environment.CurrentDirectory;
|
var cwd = Environment.CurrentDirectory;
|
||||||
bool useOsuTK = args.Contains("--tk");
|
|
||||||
|
|
||||||
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useOsuTK: useOsuTK))
|
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
|
||||||
{
|
{
|
||||||
host.ExceptionThrown += handleException;
|
host.ExceptionThrown += handleException;
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Desktop.Updater
|
|||||||
Splat.Locator.CurrentMutable.Register(() => new SquirrelLogger(), typeof(Splat.ILogger));
|
Splat.Locator.CurrentMutable.Register(() => new SquirrelLogger(), typeof(Splat.ILogger));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task<bool> PerformUpdateCheck() => await checkForUpdateAsync();
|
protected override async Task<bool> PerformUpdateCheck() => await checkForUpdateAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
private async Task<bool> checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
|
private async Task<bool> checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
|
||||||
{
|
{
|
||||||
@ -51,9 +51,9 @@ namespace osu.Desktop.Updater
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
updateManager ??= await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true);
|
updateManager ??= await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true).ConfigureAwait(false);
|
||||||
|
|
||||||
var info = await updateManager.CheckForUpdate(!useDeltaPatching);
|
var info = await updateManager.CheckForUpdate(!useDeltaPatching).ConfigureAwait(false);
|
||||||
|
|
||||||
if (info.ReleasesToApply.Count == 0)
|
if (info.ReleasesToApply.Count == 0)
|
||||||
{
|
{
|
||||||
@ -79,12 +79,12 @@ namespace osu.Desktop.Updater
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f);
|
await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f).ConfigureAwait(false);
|
||||||
|
|
||||||
notification.Progress = 0;
|
notification.Progress = 0;
|
||||||
notification.Text = @"Installing update...";
|
notification.Text = @"Installing update...";
|
||||||
|
|
||||||
await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f);
|
await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f).ConfigureAwait(false);
|
||||||
|
|
||||||
notification.State = ProgressNotificationState.Completed;
|
notification.State = ProgressNotificationState.Completed;
|
||||||
updatePending = true;
|
updatePending = true;
|
||||||
@ -97,7 +97,7 @@ namespace osu.Desktop.Updater
|
|||||||
|
|
||||||
// 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.
|
||||||
await checkForUpdateAsync(false, notification);
|
await checkForUpdateAsync(false, notification).ConfigureAwait(false);
|
||||||
scheduleRecheck = false;
|
scheduleRecheck = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -116,7 +116,7 @@ namespace osu.Desktop.Updater
|
|||||||
if (scheduleRecheck)
|
if (scheduleRecheck)
|
||||||
{
|
{
|
||||||
// check again in 30 minutes.
|
// check again in 30 minutes.
|
||||||
Scheduler.AddDelayed(async () => await checkForUpdateAsync(), 60000 * 30);
|
Scheduler.AddDelayed(async () => await checkForUpdateAsync().ConfigureAwait(false), 60000 * 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
LocalConfig.Set(OsuSetting.IncreaseFirstObjectVisibility, false);
|
LocalConfig.SetValue(OsuSetting.IncreaseFirstObjectVisibility, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -202,7 +202,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
public void TestHitLightingColour()
|
public void TestHitLightingColour()
|
||||||
{
|
{
|
||||||
var fruitColour = SkinConfiguration.DefaultComboColours[1];
|
var fruitColour = SkinConfiguration.DefaultComboColours[1];
|
||||||
AddStep("enable hit lighting", () => config.Set(OsuSetting.HitLighting, true));
|
AddStep("enable hit lighting", () => config.SetValue(OsuSetting.HitLighting, true));
|
||||||
AddStep("catch fruit", () => attemptCatch(new Fruit()));
|
AddStep("catch fruit", () => attemptCatch(new Fruit()));
|
||||||
AddAssert("correct hit lighting colour", () =>
|
AddAssert("correct hit lighting colour", () =>
|
||||||
catcher.ChildrenOfType<HitExplosion>().First()?.ObjectColour == fruitColour);
|
catcher.ChildrenOfType<HitExplosion>().First()?.ObjectColour == fruitColour);
|
||||||
@ -211,7 +211,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestHitLightingDisabled()
|
public void TestHitLightingDisabled()
|
||||||
{
|
{
|
||||||
AddStep("disable hit lighting", () => config.Set(OsuSetting.HitLighting, false));
|
AddStep("disable hit lighting", () => config.SetValue(OsuSetting.HitLighting, false));
|
||||||
AddStep("catch fruit", () => attemptCatch(new Fruit()));
|
AddStep("catch fruit", () => attemptCatch(new Fruit()));
|
||||||
AddAssert("no hit lighting", () => !catcher.ChildrenOfType<HitExplosion>().Any());
|
AddAssert("no hit lighting", () => !catcher.ChildrenOfType<HitExplosion>().Any());
|
||||||
}
|
}
|
||||||
|
@ -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.8.3" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -21,6 +21,7 @@ using osu.Game.Rulesets.Difficulty;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using System;
|
using System;
|
||||||
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Game.Rulesets.Catch.Skinning.Legacy;
|
using osu.Game.Rulesets.Catch.Skinning.Legacy;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -50,40 +51,40 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
|
|
||||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||||
{
|
{
|
||||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
if (mods.HasFlagFast(LegacyMods.Nightcore))
|
||||||
yield return new CatchModNightcore();
|
yield return new CatchModNightcore();
|
||||||
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
else if (mods.HasFlagFast(LegacyMods.DoubleTime))
|
||||||
yield return new CatchModDoubleTime();
|
yield return new CatchModDoubleTime();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Perfect))
|
if (mods.HasFlagFast(LegacyMods.Perfect))
|
||||||
yield return new CatchModPerfect();
|
yield return new CatchModPerfect();
|
||||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
else if (mods.HasFlagFast(LegacyMods.SuddenDeath))
|
||||||
yield return new CatchModSuddenDeath();
|
yield return new CatchModSuddenDeath();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Cinema))
|
if (mods.HasFlagFast(LegacyMods.Cinema))
|
||||||
yield return new CatchModCinema();
|
yield return new CatchModCinema();
|
||||||
else if (mods.HasFlag(LegacyMods.Autoplay))
|
else if (mods.HasFlagFast(LegacyMods.Autoplay))
|
||||||
yield return new CatchModAutoplay();
|
yield return new CatchModAutoplay();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Easy))
|
if (mods.HasFlagFast(LegacyMods.Easy))
|
||||||
yield return new CatchModEasy();
|
yield return new CatchModEasy();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Flashlight))
|
if (mods.HasFlagFast(LegacyMods.Flashlight))
|
||||||
yield return new CatchModFlashlight();
|
yield return new CatchModFlashlight();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.HalfTime))
|
if (mods.HasFlagFast(LegacyMods.HalfTime))
|
||||||
yield return new CatchModHalfTime();
|
yield return new CatchModHalfTime();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.HardRock))
|
if (mods.HasFlagFast(LegacyMods.HardRock))
|
||||||
yield return new CatchModHardRock();
|
yield return new CatchModHardRock();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Hidden))
|
if (mods.HasFlagFast(LegacyMods.Hidden))
|
||||||
yield return new CatchModHidden();
|
yield return new CatchModHidden();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.NoFail))
|
if (mods.HasFlagFast(LegacyMods.NoFail))
|
||||||
yield return new CatchModNoFail();
|
yield return new CatchModNoFail();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Relax))
|
if (mods.HasFlagFast(LegacyMods.Relax))
|
||||||
yield return new CatchModRelax();
|
yield return new CatchModRelax();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap)
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
|
||||||
{
|
{
|
||||||
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
|
|
||||||
return new Skill[]
|
return new Skill[]
|
||||||
{
|
{
|
||||||
new Movement(halfCatcherWidth),
|
new Movement(mods, halfCatcherWidth),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||||
{
|
{
|
||||||
@ -25,7 +26,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
private float lastDistanceMoved;
|
private float lastDistanceMoved;
|
||||||
private double lastStrainTime;
|
private double lastStrainTime;
|
||||||
|
|
||||||
public Movement(float halfCatcherWidth)
|
public Movement(Mod[] mods, float halfCatcherWidth)
|
||||||
|
: base(mods)
|
||||||
{
|
{
|
||||||
HalfCatcherWidth = halfCatcherWidth;
|
HalfCatcherWidth = halfCatcherWidth;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
|
Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
|
||||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released");
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
|
Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
|
||||||
|
|
||||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||||
|
|
||||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
||||||
@ -148,9 +148,9 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames");
|
Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames");
|
||||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
|
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
|
||||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time");
|
Assert.AreEqual(3000, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time");
|
||||||
Assert.AreEqual(2000, generated.Frames[frame_offset + 1].Time, "Incorrect second note hit time");
|
Assert.AreEqual(2000, generated.Frames[frame_offset + 1].Time, "Incorrect second note hit time");
|
||||||
Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
|
Assert.AreEqual(4000, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key1), "Key1 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key1), "Key1 has not been released");
|
||||||
@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
// | | |
|
// | | |
|
||||||
|
|
||||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY });
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||||
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
|
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
|
||||||
|
|
||||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
@ -5,11 +5,13 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -345,6 +347,14 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||||
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddUntilStep("wait for head", () => currentPlayer.GameplayClockContainer.GameplayClock.CurrentTime >= time_head);
|
||||||
|
AddAssert("head is visible",
|
||||||
|
() => currentPlayer.ChildrenOfType<DrawableHoldNote>()
|
||||||
|
.Single(note => note.HitObject == beatmap.HitObjects[0])
|
||||||
|
.Head
|
||||||
|
.Alpha == 1);
|
||||||
|
|
||||||
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
|
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,6 +362,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
{
|
{
|
||||||
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
||||||
|
|
||||||
|
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
|
||||||
|
|
||||||
protected override bool PauseOnFocusLost => false;
|
protected override bool PauseOnFocusLost => false;
|
||||||
|
|
||||||
public ScoreAccessibleReplayPlayer(Score score)
|
public ScoreAccessibleReplayPlayer(Score score)
|
||||||
|
@ -6,6 +6,7 @@ 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.Extensions.EnumExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -97,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
private bool verifyAnchors(DrawableHitObject hitObject, Anchor expectedAnchor)
|
private bool verifyAnchors(DrawableHitObject hitObject, Anchor expectedAnchor)
|
||||||
=> hitObject.Anchor.HasFlag(expectedAnchor) && hitObject.Origin.HasFlag(expectedAnchor);
|
=> hitObject.Anchor.HasFlagFast(expectedAnchor) && hitObject.Origin.HasFlagFast(expectedAnchor);
|
||||||
|
|
||||||
private bool verifyAnchors(DrawableHoldNote holdNote, Anchor expectedAnchor)
|
private bool verifyAnchors(DrawableHoldNote holdNote, Anchor expectedAnchor)
|
||||||
=> verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor));
|
=> verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor));
|
||||||
|
@ -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.8.3" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
@ -141,7 +142,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||||
return generateNRandomNotes(StartTime, 0.78, 0.3, 0);
|
return generateNRandomNotes(StartTime, 0.78, 0.3, 0);
|
||||||
|
|
||||||
return generateNRandomNotes(StartTime, 0.85, 0.36, 0.03);
|
return generateNRandomNotes(StartTime, 0.85, 0.36, 0.03);
|
||||||
@ -149,7 +150,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (ConversionDifficulty > 4)
|
if (ConversionDifficulty > 4)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||||
return generateNRandomNotes(StartTime, 0.43, 0.08, 0);
|
return generateNRandomNotes(StartTime, 0.43, 0.08, 0);
|
||||||
|
|
||||||
return generateNRandomNotes(StartTime, 0.56, 0.18, 0);
|
return generateNRandomNotes(StartTime, 0.56, 0.18, 0);
|
||||||
@ -157,13 +158,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (ConversionDifficulty > 2.5)
|
if (ConversionDifficulty > 2.5)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||||
return generateNRandomNotes(StartTime, 0.3, 0, 0);
|
return generateNRandomNotes(StartTime, 0.3, 0, 0);
|
||||||
|
|
||||||
return generateNRandomNotes(StartTime, 0.37, 0.08, 0);
|
return generateNRandomNotes(StartTime, 0.37, 0.08, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||||
return generateNRandomNotes(StartTime, 0.17, 0, 0);
|
return generateNRandomNotes(StartTime, 0.17, 0, 0);
|
||||||
|
|
||||||
return generateNRandomNotes(StartTime, 0.27, 0, 0);
|
return generateNRandomNotes(StartTime, 0.27, 0, 0);
|
||||||
@ -221,7 +222,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
if (convertType.HasFlagFast(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||||
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
|
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
|
||||||
|
|
||||||
int lastColumn = nextColumn;
|
int lastColumn = nextColumn;
|
||||||
@ -373,7 +374,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
static bool isDoubleSample(HitSampleInfo sample) => sample.Name == HitSampleInfo.HIT_CLAP || sample.Name == HitSampleInfo.HIT_FINISH;
|
static bool isDoubleSample(HitSampleInfo sample) => sample.Name == HitSampleInfo.HIT_CLAP || sample.Name == HitSampleInfo.HIT_FINISH;
|
||||||
|
|
||||||
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
|
bool canGenerateTwoNotes = !convertType.HasFlagFast(PatternType.LowProbability);
|
||||||
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(StartTime).Any(isDoubleSample);
|
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(StartTime).Any(isDoubleSample);
|
||||||
|
|
||||||
if (canGenerateTwoNotes)
|
if (canGenerateTwoNotes)
|
||||||
@ -406,7 +407,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
int endTime = startTime + SegmentDuration * SpanCount;
|
int endTime = startTime + SegmentDuration * SpanCount;
|
||||||
|
|
||||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
if (convertType.HasFlagFast(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||||
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
|
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
|
||||||
|
|
||||||
for (int i = 0; i < columnRepeat; i++)
|
for (int i = 0; i < columnRepeat; i++)
|
||||||
@ -435,7 +436,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
if (convertType.HasFlagFast(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||||
holdColumn = FindAvailableColumn(holdColumn, PreviousPattern);
|
holdColumn = FindAvailableColumn(holdColumn, PreviousPattern);
|
||||||
|
|
||||||
// Create the hold note
|
// Create the hold note
|
||||||
|
@ -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.EnumExtensions;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
else
|
else
|
||||||
convertType |= PatternType.LowProbability;
|
convertType |= PatternType.LowProbability;
|
||||||
|
|
||||||
if (!convertType.HasFlag(PatternType.KeepSingle))
|
if (!convertType.HasFlagFast(PatternType.KeepSingle))
|
||||||
{
|
{
|
||||||
if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && TotalColumns != 8)
|
if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && TotalColumns != 8)
|
||||||
convertType |= PatternType.Mirror;
|
convertType |= PatternType.Mirror;
|
||||||
@ -101,7 +102,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.Reverse) && PreviousPattern.HitObjects.Any())
|
if (convertType.HasFlagFast(PatternType.Reverse) && PreviousPattern.HitObjects.Any())
|
||||||
{
|
{
|
||||||
// Generate a new pattern by copying the last hit objects in reverse-column order
|
// Generate a new pattern by copying the last hit objects in reverse-column order
|
||||||
for (int i = RandomStart; i < TotalColumns; i++)
|
for (int i = RandomStart; i < TotalColumns; i++)
|
||||||
@ -113,11 +114,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
|
if (convertType.HasFlagFast(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
|
||||||
// If we convert to 7K + 1, let's not overload the special key
|
// If we convert to 7K + 1, let's not overload the special key
|
||||||
&& (TotalColumns != 8 || lastColumn != 0)
|
&& (TotalColumns != 8 || lastColumn != 0)
|
||||||
// Make sure the last column was not the centre column
|
// Make sure the last column was not the centre column
|
||||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||||
{
|
{
|
||||||
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
||||||
int column = RandomStart + TotalColumns - lastColumn - 1;
|
int column = RandomStart + TotalColumns - lastColumn - 1;
|
||||||
@ -126,7 +127,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.ForceStack) && PreviousPattern.HitObjects.Any())
|
if (convertType.HasFlagFast(PatternType.ForceStack) && PreviousPattern.HitObjects.Any())
|
||||||
{
|
{
|
||||||
// Generate a new pattern by placing on the already filled columns
|
// Generate a new pattern by placing on the already filled columns
|
||||||
for (int i = RandomStart; i < TotalColumns; i++)
|
for (int i = RandomStart; i < TotalColumns; i++)
|
||||||
@ -140,7 +141,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (PreviousPattern.HitObjects.Count() == 1)
|
if (PreviousPattern.HitObjects.Count() == 1)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.Stair))
|
if (convertType.HasFlagFast(PatternType.Stair))
|
||||||
{
|
{
|
||||||
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
|
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
|
||||||
int targetColumn = lastColumn + 1;
|
int targetColumn = lastColumn + 1;
|
||||||
@ -151,7 +152,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.ReverseStair))
|
if (convertType.HasFlagFast(PatternType.ReverseStair))
|
||||||
{
|
{
|
||||||
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
|
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
|
||||||
int targetColumn = lastColumn - 1;
|
int targetColumn = lastColumn - 1;
|
||||||
@ -163,10 +164,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.KeepSingle))
|
if (convertType.HasFlagFast(PatternType.KeepSingle))
|
||||||
return generateRandomNotes(1);
|
return generateRandomNotes(1);
|
||||||
|
|
||||||
if (convertType.HasFlag(PatternType.Mirror))
|
if (convertType.HasFlagFast(PatternType.Mirror))
|
||||||
{
|
{
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
return generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
return generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
||||||
@ -178,7 +179,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||||
return generateRandomPattern(0.78, 0.42, 0, 0);
|
return generateRandomPattern(0.78, 0.42, 0, 0);
|
||||||
|
|
||||||
return generateRandomPattern(1, 0.62, 0, 0);
|
return generateRandomPattern(1, 0.62, 0, 0);
|
||||||
@ -186,7 +187,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (ConversionDifficulty > 4)
|
if (ConversionDifficulty > 4)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||||
return generateRandomPattern(0.35, 0.08, 0, 0);
|
return generateRandomPattern(0.35, 0.08, 0, 0);
|
||||||
|
|
||||||
return generateRandomPattern(0.52, 0.15, 0, 0);
|
return generateRandomPattern(0.52, 0.15, 0, 0);
|
||||||
@ -194,7 +195,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (ConversionDifficulty > 2)
|
if (ConversionDifficulty > 2)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.LowProbability))
|
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||||
return generateRandomPattern(0.18, 0, 0, 0);
|
return generateRandomPattern(0.18, 0, 0, 0);
|
||||||
|
|
||||||
return generateRandomPattern(0.45, 0, 0, 0);
|
return generateRandomPattern(0.45, 0, 0, 0);
|
||||||
@ -207,9 +208,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
foreach (var obj in p.HitObjects)
|
foreach (var obj in p.HitObjects)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.Stair) && obj.Column == TotalColumns - 1)
|
if (convertType.HasFlagFast(PatternType.Stair) && obj.Column == TotalColumns - 1)
|
||||||
StairType = PatternType.ReverseStair;
|
StairType = PatternType.ReverseStair;
|
||||||
if (convertType.HasFlag(PatternType.ReverseStair) && obj.Column == RandomStart)
|
if (convertType.HasFlagFast(PatternType.ReverseStair) && obj.Column == RandomStart)
|
||||||
StairType = PatternType.Stair;
|
StairType = PatternType.Stair;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
{
|
{
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
bool allowStacking = !convertType.HasFlag(PatternType.ForceNotStack);
|
bool allowStacking = !convertType.HasFlagFast(PatternType.ForceNotStack);
|
||||||
|
|
||||||
if (!allowStacking)
|
if (!allowStacking)
|
||||||
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
|
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
|
||||||
@ -249,7 +250,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
int getNextColumn(int last)
|
int getNextColumn(int last)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.Gathered))
|
if (convertType.HasFlagFast(PatternType.Gathered))
|
||||||
{
|
{
|
||||||
last++;
|
last++;
|
||||||
if (last == TotalColumns)
|
if (last == TotalColumns)
|
||||||
@ -296,7 +297,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3)
|
private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3)
|
||||||
{
|
{
|
||||||
if (convertType.HasFlag(PatternType.ForceNotStack))
|
if (convertType.HasFlagFast(PatternType.ForceNotStack))
|
||||||
return generateRandomPattern(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
|
return generateRandomPattern(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
|
||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
|||||||
{
|
{
|
||||||
base.InitialiseDefaults();
|
base.InitialiseDefaults();
|
||||||
|
|
||||||
Set(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5);
|
SetDefault(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5);
|
||||||
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||||
|
@ -68,9 +68,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
|
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
|
||||||
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;
|
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
||||||
{
|
{
|
||||||
new Strain(((ManiaBeatmap)beatmap).TotalColumns)
|
new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods
|
protected override Mod[] DifficultyAdjustmentMods
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Utils;
|
|||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||||
@ -24,7 +25,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
|||||||
private double individualStrain;
|
private double individualStrain;
|
||||||
private double overallStrain;
|
private double overallStrain;
|
||||||
|
|
||||||
public Strain(int totalColumns)
|
public Strain(Mod[] mods, int totalColumns)
|
||||||
|
: base(mods)
|
||||||
{
|
{
|
||||||
holdEndTimes = new double[totalColumns];
|
holdEndTimes = new double[totalColumns];
|
||||||
individualStrains = new double[totalColumns];
|
individualStrains = new double[totalColumns];
|
||||||
|
@ -45,6 +45,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
int minColumn = int.MaxValue;
|
int minColumn = int.MaxValue;
|
||||||
int maxColumn = int.MinValue;
|
int maxColumn = int.MinValue;
|
||||||
|
|
||||||
|
// find min/max in an initial pass before actually performing the movement.
|
||||||
foreach (var obj in EditorBeatmap.SelectedHitObjects.OfType<ManiaHitObject>())
|
foreach (var obj in EditorBeatmap.SelectedHitObjects.OfType<ManiaHitObject>())
|
||||||
{
|
{
|
||||||
if (obj.Column < minColumn)
|
if (obj.Column < minColumn)
|
||||||
@ -55,8 +56,11 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
|
|
||||||
columnDelta = Math.Clamp(columnDelta, -minColumn, maniaPlayfield.TotalColumns - 1 - maxColumn);
|
columnDelta = Math.Clamp(columnDelta, -minColumn, maniaPlayfield.TotalColumns - 1 - maxColumn);
|
||||||
|
|
||||||
foreach (var obj in EditorBeatmap.SelectedHitObjects.OfType<ManiaHitObject>())
|
EditorBeatmap.PerformOnSelection(h =>
|
||||||
obj.Column += columnDelta;
|
{
|
||||||
|
if (h is ManiaHitObject maniaObj)
|
||||||
|
maniaObj.Column += columnDelta;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
@ -59,76 +60,76 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||||
{
|
{
|
||||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
if (mods.HasFlagFast(LegacyMods.Nightcore))
|
||||||
yield return new ManiaModNightcore();
|
yield return new ManiaModNightcore();
|
||||||
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
else if (mods.HasFlagFast(LegacyMods.DoubleTime))
|
||||||
yield return new ManiaModDoubleTime();
|
yield return new ManiaModDoubleTime();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Perfect))
|
if (mods.HasFlagFast(LegacyMods.Perfect))
|
||||||
yield return new ManiaModPerfect();
|
yield return new ManiaModPerfect();
|
||||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
else if (mods.HasFlagFast(LegacyMods.SuddenDeath))
|
||||||
yield return new ManiaModSuddenDeath();
|
yield return new ManiaModSuddenDeath();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Cinema))
|
if (mods.HasFlagFast(LegacyMods.Cinema))
|
||||||
yield return new ManiaModCinema();
|
yield return new ManiaModCinema();
|
||||||
else if (mods.HasFlag(LegacyMods.Autoplay))
|
else if (mods.HasFlagFast(LegacyMods.Autoplay))
|
||||||
yield return new ManiaModAutoplay();
|
yield return new ManiaModAutoplay();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Easy))
|
if (mods.HasFlagFast(LegacyMods.Easy))
|
||||||
yield return new ManiaModEasy();
|
yield return new ManiaModEasy();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.FadeIn))
|
if (mods.HasFlagFast(LegacyMods.FadeIn))
|
||||||
yield return new ManiaModFadeIn();
|
yield return new ManiaModFadeIn();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Flashlight))
|
if (mods.HasFlagFast(LegacyMods.Flashlight))
|
||||||
yield return new ManiaModFlashlight();
|
yield return new ManiaModFlashlight();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.HalfTime))
|
if (mods.HasFlagFast(LegacyMods.HalfTime))
|
||||||
yield return new ManiaModHalfTime();
|
yield return new ManiaModHalfTime();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.HardRock))
|
if (mods.HasFlagFast(LegacyMods.HardRock))
|
||||||
yield return new ManiaModHardRock();
|
yield return new ManiaModHardRock();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Hidden))
|
if (mods.HasFlagFast(LegacyMods.Hidden))
|
||||||
yield return new ManiaModHidden();
|
yield return new ManiaModHidden();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Key1))
|
if (mods.HasFlagFast(LegacyMods.Key1))
|
||||||
yield return new ManiaModKey1();
|
yield return new ManiaModKey1();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Key2))
|
if (mods.HasFlagFast(LegacyMods.Key2))
|
||||||
yield return new ManiaModKey2();
|
yield return new ManiaModKey2();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Key3))
|
if (mods.HasFlagFast(LegacyMods.Key3))
|
||||||
yield return new ManiaModKey3();
|
yield return new ManiaModKey3();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Key4))
|
if (mods.HasFlagFast(LegacyMods.Key4))
|
||||||
yield return new ManiaModKey4();
|
yield return new ManiaModKey4();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Key5))
|
if (mods.HasFlagFast(LegacyMods.Key5))
|
||||||
yield return new ManiaModKey5();
|
yield return new ManiaModKey5();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Key6))
|
if (mods.HasFlagFast(LegacyMods.Key6))
|
||||||
yield return new ManiaModKey6();
|
yield return new ManiaModKey6();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Key7))
|
if (mods.HasFlagFast(LegacyMods.Key7))
|
||||||
yield return new ManiaModKey7();
|
yield return new ManiaModKey7();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Key8))
|
if (mods.HasFlagFast(LegacyMods.Key8))
|
||||||
yield return new ManiaModKey8();
|
yield return new ManiaModKey8();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Key9))
|
if (mods.HasFlagFast(LegacyMods.Key9))
|
||||||
yield return new ManiaModKey9();
|
yield return new ManiaModKey9();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.KeyCoop))
|
if (mods.HasFlagFast(LegacyMods.KeyCoop))
|
||||||
yield return new ManiaModDualStages();
|
yield return new ManiaModDualStages();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.NoFail))
|
if (mods.HasFlagFast(LegacyMods.NoFail))
|
||||||
yield return new ManiaModNoFail();
|
yield return new ManiaModNoFail();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Random))
|
if (mods.HasFlagFast(LegacyMods.Random))
|
||||||
yield return new ManiaModRandom();
|
yield return new ManiaModRandom();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Mirror))
|
if (mods.HasFlagFast(LegacyMods.Mirror))
|
||||||
yield return new ManiaModMirror();
|
yield return new ManiaModMirror();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
using System;
|
||||||
using osu.Game.Graphics;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModFadeIn : ManiaModHidden
|
public class ManiaModFadeIn : ManiaModPlayfieldCover
|
||||||
{
|
{
|
||||||
public override string Name => "Fade In";
|
public override string Name => "Fade In";
|
||||||
public override string Acronym => "FI";
|
public override string Acronym => "FI";
|
||||||
public override IconUsage? Icon => OsuIcon.ModHidden;
|
|
||||||
public override string Description => @"Keys appear out of nowhere!";
|
public override string Description => @"Keys appear out of nowhere!";
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModHidden)).ToArray();
|
||||||
|
|
||||||
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AlongScroll;
|
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AlongScroll;
|
||||||
}
|
}
|
||||||
|
@ -3,43 +3,17 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModHidden : ModHidden, IApplicableToDrawableRuleset<ManiaHitObject>
|
public class ManiaModHidden : ManiaModPlayfieldCover
|
||||||
{
|
{
|
||||||
public override string Description => @"Keys fade out before you hit them!";
|
public override string Description => @"Keys fade out before you hit them!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
|
|
||||||
|
|
||||||
/// <summary>
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModFadeIn)).ToArray();
|
||||||
/// The direction in which the cover should expand.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
|
||||||
|
|
||||||
public virtual void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
||||||
{
|
|
||||||
ManiaPlayfield maniaPlayfield = (ManiaPlayfield)drawableRuleset.Playfield;
|
|
||||||
|
|
||||||
foreach (Column column in maniaPlayfield.Stages.SelectMany(stage => stage.Columns))
|
|
||||||
{
|
|
||||||
HitObjectContainer hoc = column.HitObjectArea.HitObjectContainer;
|
|
||||||
Container hocParent = (Container)hoc.Parent;
|
|
||||||
|
|
||||||
hocParent.Remove(hoc);
|
|
||||||
hocParent.Add(new PlayfieldCoveringWrapper(hoc).With(c =>
|
|
||||||
{
|
|
||||||
c.RelativeSizeAxes = Axes.Both;
|
|
||||||
c.Direction = ExpandDirection;
|
|
||||||
c.Coverage = 0.5f;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
43
osu.Game.Rulesets.Mania/Mods/ManiaModPlayfieldCover.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public abstract class ManiaModPlayfieldCover : ModHidden, IApplicableToDrawableRuleset<ManiaHitObject>
|
||||||
|
{
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The direction in which the cover should expand.
|
||||||
|
/// </summary>
|
||||||
|
protected abstract CoverExpandDirection ExpandDirection { get; }
|
||||||
|
|
||||||
|
public virtual void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
ManiaPlayfield maniaPlayfield = (ManiaPlayfield)drawableRuleset.Playfield;
|
||||||
|
|
||||||
|
foreach (Column column in maniaPlayfield.Stages.SelectMany(stage => stage.Columns))
|
||||||
|
{
|
||||||
|
HitObjectContainer hoc = column.HitObjectArea.HitObjectContainer;
|
||||||
|
Container hocParent = (Container)hoc.Parent;
|
||||||
|
|
||||||
|
hocParent.Remove(hoc);
|
||||||
|
hocParent.Add(new PlayfieldCoveringWrapper(hoc).With(c =>
|
||||||
|
{
|
||||||
|
c.RelativeSizeAxes = Axes.Both;
|
||||||
|
c.Direction = ExpandDirection;
|
||||||
|
c.Coverage = 0.5f;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +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 osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -25,6 +27,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
LifetimeEnd = LifetimeStart + 30000;
|
LifetimeEnd = LifetimeStart + 30000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
{
|
||||||
|
// suppress the base call explicitly.
|
||||||
|
// the hold note head should never change its visual state on its own due to the "freezing" mechanic
|
||||||
|
// (when hit, it remains visible in place at the judgement line; when dropped, it will scroll past the line).
|
||||||
|
// it will be hidden along with its parenting hold note when required.
|
||||||
|
}
|
||||||
|
|
||||||
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
|
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
|
||||||
|
|
||||||
public override void OnReleased(ManiaAction action)
|
public override void OnReleased(ManiaAction action)
|
||||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
@ -85,20 +86,28 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
{
|
{
|
||||||
var currentObject = Beatmap.HitObjects[i];
|
var currentObject = Beatmap.HitObjects[i];
|
||||||
var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button
|
var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button
|
||||||
|
var releaseTime = calculateReleaseTime(currentObject, nextObjectInColumn);
|
||||||
double endTime = currentObject.GetEndTime();
|
|
||||||
|
|
||||||
bool canDelayKeyUp = nextObjectInColumn == null ||
|
|
||||||
nextObjectInColumn.StartTime > endTime + RELEASE_DELAY;
|
|
||||||
|
|
||||||
double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9;
|
|
||||||
|
|
||||||
yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column };
|
yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column };
|
||||||
|
|
||||||
yield return new ReleasePoint { Time = endTime + calculatedDelay, Column = currentObject.Column };
|
yield return new ReleasePoint { Time = releaseTime, Column = currentObject.Column };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double calculateReleaseTime(HitObject currentObject, HitObject nextObject)
|
||||||
|
{
|
||||||
|
double endTime = currentObject.GetEndTime();
|
||||||
|
|
||||||
|
if (currentObject is HoldNote)
|
||||||
|
// hold note releases must be timed exactly.
|
||||||
|
return endTime;
|
||||||
|
|
||||||
|
bool canDelayKeyUpFully = nextObject == null ||
|
||||||
|
nextObject.StartTime > endTime + RELEASE_DELAY;
|
||||||
|
|
||||||
|
return endTime + (canDelayKeyUpFully ? RELEASE_DELAY : (nextObject.StartTime - endTime) * 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
protected override HitObject GetNextObject(int currentIndex)
|
protected override HitObject GetNextObject(int currentIndex)
|
||||||
{
|
{
|
||||||
int desiredColumn = Beatmap.HitObjects[currentIndex].Column;
|
int desiredColumn = Beatmap.HitObjects[currentIndex].Column;
|
||||||
|
@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
public class LegacyHitExplosion : LegacyManiaColumnElement, IHitExplosion
|
public class LegacyHitExplosion : LegacyManiaColumnElement, IHitExplosion
|
||||||
{
|
{
|
||||||
|
public const double FADE_IN_DURATION = 80;
|
||||||
|
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
private Drawable explosion;
|
private Drawable explosion;
|
||||||
@ -72,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
|
|
||||||
(explosion as IFramedAnimation)?.GotoFrame(0);
|
(explosion as IFramedAnimation)?.GotoFrame(0);
|
||||||
|
|
||||||
explosion?.FadeInFromZero(80)
|
explosion?.FadeInFromZero(FADE_IN_DURATION)
|
||||||
.Then().FadeOut(120);
|
.Then().FadeOut(120);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,8 +101,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
if (action == column.Action.Value)
|
if (action == column.Action.Value)
|
||||||
{
|
{
|
||||||
upSprite.FadeTo(1);
|
upSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(1);
|
||||||
downSprite.FadeTo(0);
|
downSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
return animation == null ? null : new LegacyManiaJudgementPiece(result, animation);
|
return animation == null ? null : new LegacyManiaJudgementPiece(result, animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Sample GetSample(ISampleInfo sampleInfo)
|
public override ISample GetSample(ISampleInfo sampleInfo)
|
||||||
{
|
{
|
||||||
// layered hit sounds never play in mania
|
// layered hit sounds never play in mania
|
||||||
if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample && legacySample.IsLayered)
|
if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample && legacySample.IsLayered)
|
||||||
|
@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
|
||||||
[TestCase(6.9311451172608853d, "diffcalc-test")]
|
[TestCase(6.9311451172574934d, "diffcalc-test")]
|
||||||
[TestCase(1.0736587013228804d, "zero-length-sliders")]
|
[TestCase(1.0736586907780401d, "zero-length-sliders")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
|
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-0.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-1.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-2.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-3.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-4.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-5.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-6.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-7.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-8.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-9.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-comma.png
Normal file
After Width: | Height: | Size: 865 B |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-dot.png
Normal file
After Width: | Height: | Size: 771 B |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-percent.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/score-x.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
@ -1,2 +1,6 @@
|
|||||||
[General]
|
[General]
|
||||||
Version: 1.0
|
Version: 1.0
|
||||||
|
|
||||||
|
[Fonts]
|
||||||
|
HitCircleOverlap: 3
|
||||||
|
ScoreOverlap: 3
|
@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestHitLightingDisabled()
|
public void TestHitLightingDisabled()
|
||||||
{
|
{
|
||||||
AddStep("hit lighting disabled", () => config.Set(OsuSetting.HitLighting, false));
|
AddStep("hit lighting disabled", () => config.SetValue(OsuSetting.HitLighting, false));
|
||||||
|
|
||||||
showResult(HitResult.Great);
|
showResult(HitResult.Great);
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestHitLightingEnabled()
|
public void TestHitLightingEnabled()
|
||||||
{
|
{
|
||||||
AddStep("hit lighting enabled", () => config.Set(OsuSetting.HitLighting, true));
|
AddStep("hit lighting enabled", () => config.SetValue(OsuSetting.HitLighting, true));
|
||||||
|
|
||||||
showResult(HitResult.Great);
|
showResult(HitResult.Great);
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
AddSliderStep("circle size", 0f, 10f, 0f, val =>
|
AddSliderStep("circle size", 0f, 10f, 0f, val =>
|
||||||
{
|
{
|
||||||
config.Set(OsuSetting.AutoCursorSize, true);
|
config.SetValue(OsuSetting.AutoCursorSize, true);
|
||||||
gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = val;
|
gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = val;
|
||||||
Scheduler.AddOnce(recreate);
|
Scheduler.AddOnce(recreate);
|
||||||
});
|
});
|
||||||
@ -64,21 +64,21 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestCase(10, 1.5f)]
|
[TestCase(10, 1.5f)]
|
||||||
public void TestSizing(int circleSize, float userScale)
|
public void TestSizing(int circleSize, float userScale)
|
||||||
{
|
{
|
||||||
AddStep($"set user scale to {userScale}", () => config.Set(OsuSetting.GameplayCursorSize, userScale));
|
AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale));
|
||||||
AddStep($"adjust cs to {circleSize}", () => gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize);
|
AddStep($"adjust cs to {circleSize}", () => gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize);
|
||||||
AddStep("turn on autosizing", () => config.Set(OsuSetting.AutoCursorSize, true));
|
AddStep("turn on autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, true));
|
||||||
|
|
||||||
AddStep("load content", loadContent);
|
AddStep("load content", loadContent);
|
||||||
|
|
||||||
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize) * userScale);
|
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize) * userScale);
|
||||||
|
|
||||||
AddStep("set user scale to 1", () => config.Set(OsuSetting.GameplayCursorSize, 1f));
|
AddStep("set user scale to 1", () => config.SetValue(OsuSetting.GameplayCursorSize, 1f));
|
||||||
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize));
|
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize));
|
||||||
|
|
||||||
AddStep("turn off autosizing", () => config.Set(OsuSetting.AutoCursorSize, false));
|
AddStep("turn off autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, false));
|
||||||
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == 1);
|
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == 1);
|
||||||
|
|
||||||
AddStep($"set user scale to {userScale}", () => config.Set(OsuSetting.GameplayCursorSize, userScale));
|
AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale));
|
||||||
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == userScale);
|
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == userScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +42,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
AddStep("enable user provider", () => testUserSkin.Enabled = true);
|
AddStep("enable user provider", () => testUserSkin.Enabled = true);
|
||||||
|
|
||||||
AddStep("enable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, true));
|
AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true));
|
||||||
checkNextHitObject("beatmap");
|
checkNextHitObject("beatmap");
|
||||||
|
|
||||||
AddStep("disable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, false));
|
AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false));
|
||||||
checkNextHitObject("user");
|
checkNextHitObject("user");
|
||||||
|
|
||||||
AddStep("disable user provider", () => testUserSkin.Enabled = false);
|
AddStep("disable user provider", () => testUserSkin.Enabled = false);
|
||||||
@ -57,20 +57,20 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
AddStep("enable user provider", () => testUserSkin.Enabled = true);
|
AddStep("enable user provider", () => testUserSkin.Enabled = true);
|
||||||
|
|
||||||
AddStep("enable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, true));
|
AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true));
|
||||||
AddStep("enable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, true));
|
AddStep("enable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, true));
|
||||||
checkNextHitObject("beatmap");
|
checkNextHitObject("beatmap");
|
||||||
|
|
||||||
AddStep("enable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, true));
|
AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true));
|
||||||
AddStep("disable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, false));
|
AddStep("disable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, false));
|
||||||
checkNextHitObject("beatmap");
|
checkNextHitObject("beatmap");
|
||||||
|
|
||||||
AddStep("disable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, false));
|
AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false));
|
||||||
AddStep("enable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, true));
|
AddStep("enable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, true));
|
||||||
checkNextHitObject("user");
|
checkNextHitObject("user");
|
||||||
|
|
||||||
AddStep("disable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, false));
|
AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false));
|
||||||
AddStep("disable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, false));
|
AddStep("disable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, false));
|
||||||
checkNextHitObject("user");
|
checkNextHitObject("user");
|
||||||
|
|
||||||
AddStep("disable user provider", () => testUserSkin.Enabled = false);
|
AddStep("disable user provider", () => testUserSkin.Enabled = false);
|
||||||
@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
||||||
|
|
||||||
public Sample GetSample(ISampleInfo sampleInfo) => null;
|
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
||||||
|
|
||||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default;
|
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default;
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => 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.8.3" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -17,10 +17,10 @@ namespace osu.Game.Rulesets.Osu.Configuration
|
|||||||
protected override void InitialiseDefaults()
|
protected override void InitialiseDefaults()
|
||||||
{
|
{
|
||||||
base.InitialiseDefaults();
|
base.InitialiseDefaults();
|
||||||
Set(OsuRulesetSetting.SnakingInSliders, true);
|
SetDefault(OsuRulesetSetting.SnakingInSliders, true);
|
||||||
Set(OsuRulesetSetting.SnakingOutSliders, true);
|
SetDefault(OsuRulesetSetting.SnakingOutSliders, true);
|
||||||
Set(OsuRulesetSetting.ShowCursorTrail, true);
|
SetDefault(OsuRulesetSetting.ShowCursorTrail, true);
|
||||||
Set(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None);
|
SetDefault(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,10 +79,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
||||||
{
|
{
|
||||||
new Aim(),
|
new Aim(mods),
|
||||||
new Speed()
|
new Speed(mods)
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
@ -17,6 +18,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
private const double angle_bonus_begin = Math.PI / 3;
|
private const double angle_bonus_begin = Math.PI / 3;
|
||||||
private const double timing_threshold = 107;
|
private const double timing_threshold = 107;
|
||||||
|
|
||||||
|
public Aim(Mod[] mods)
|
||||||
|
: base(mods)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override double SkillMultiplier => 26.25;
|
protected override double SkillMultiplier => 26.25;
|
||||||
protected override double StrainDecayBase => 0.15;
|
protected override double StrainDecayBase => 0.15;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
@ -27,6 +28,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
private const double max_speed_bonus = 45; // ~330BPM
|
private const double max_speed_bonus = 45; // ~330BPM
|
||||||
private const double speed_balancing_factor = 40;
|
private const double speed_balancing_factor = 40;
|
||||||
|
|
||||||
|
public Speed(Mod[] mods)
|
||||||
|
: base(mods)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
{
|
{
|
||||||
if (current.BaseObject is Spinner)
|
if (current.BaseObject is Spinner)
|
||||||
|
@ -7,11 +7,13 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -23,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A visualisation of a single <see cref="PathControlPoint"/> in a <see cref="Slider"/>.
|
/// A visualisation of a single <see cref="PathControlPoint"/> in a <see cref="Slider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PathControlPointPiece : BlueprintPiece<Slider>
|
public class PathControlPointPiece : BlueprintPiece<Slider>, IHasTooltip
|
||||||
{
|
{
|
||||||
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
||||||
|
|
||||||
@ -195,7 +197,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
||||||
|
|
||||||
Color4 colour = ControlPoint.Type.Value != null ? colours.Red : colours.Yellow;
|
Color4 colour = getColourFromNodeType();
|
||||||
|
|
||||||
if (IsHovered || IsSelected.Value)
|
if (IsHovered || IsSelected.Value)
|
||||||
colour = colour.Lighten(1);
|
colour = colour.Lighten(1);
|
||||||
@ -203,5 +205,28 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
marker.Colour = colour;
|
marker.Colour = colour;
|
||||||
marker.Scale = new Vector2(slider.Scale);
|
marker.Scale = new Vector2(slider.Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Color4 getColourFromNodeType()
|
||||||
|
{
|
||||||
|
if (!(ControlPoint.Type.Value is PathType pathType))
|
||||||
|
return colours.Yellow;
|
||||||
|
|
||||||
|
switch (pathType)
|
||||||
|
{
|
||||||
|
case PathType.Catmull:
|
||||||
|
return colours.Seafoam;
|
||||||
|
|
||||||
|
case PathType.Bezier:
|
||||||
|
return colours.Pink;
|
||||||
|
|
||||||
|
case PathType.PerfectCurve:
|
||||||
|
return colours.PurpleDark;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return colours.Red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TooltipText => ControlPoint.Type.Value.ToString() ?? string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -28,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
protected SliderBodyPiece BodyPiece { get; private set; }
|
protected SliderBodyPiece BodyPiece { get; private set; }
|
||||||
protected SliderCircleSelectionBlueprint HeadBlueprint { get; private set; }
|
protected SliderCircleSelectionBlueprint HeadBlueprint { get; private set; }
|
||||||
protected SliderCircleSelectionBlueprint TailBlueprint { get; private set; }
|
protected SliderCircleSelectionBlueprint TailBlueprint { get; private set; }
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
protected PathControlPointVisualiser ControlPointVisualiser { get; private set; }
|
protected PathControlPointVisualiser ControlPointVisualiser { get; private set; }
|
||||||
|
|
||||||
private readonly DrawableSlider slider;
|
private readonly DrawableSlider slider;
|
||||||
@ -114,6 +117,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
// throw away frame buffers on deselection.
|
// throw away frame buffers on deselection.
|
||||||
ControlPointVisualiser?.Expire();
|
ControlPointVisualiser?.Expire();
|
||||||
|
ControlPointVisualiser = null;
|
||||||
|
|
||||||
BodyPiece.RecyclePath();
|
BodyPiece.RecyclePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,11 @@ namespace osu.Game.Rulesets.Osu.Judgements
|
|||||||
/// </example>
|
/// </example>
|
||||||
public float RateAdjustedRotation;
|
public float RateAdjustedRotation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time instant at which the spin was started (the first user input which caused an increase in spin).
|
||||||
|
/// </summary>
|
||||||
|
public double? TimeStarted;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time instant at which the spinner has been completed (the user has executed all required spins).
|
/// Time instant at which the spinner has been completed (the user has executed all required spins).
|
||||||
/// Will be null if all required spins haven't been completed.
|
/// Will be null if all required spins haven't been completed.
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override ModType Type => ModType.Automation;
|
public override ModType Type => ModType.Automation;
|
||||||
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
|
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay) };
|
||||||
|
|
||||||
public bool PerformFail() => false;
|
public bool PerformFail() => false;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ using osu.Game.Rulesets.Osu.Skinning.Default;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
internal class OsuModTraceable : ModWithVisibilityAdjustment
|
public class OsuModTraceable : ModWithVisibilityAdjustment
|
||||||
{
|
{
|
||||||
public override string Name => "Traceable";
|
public override string Name => "Traceable";
|
||||||
public override string Acronym => "TC";
|
public override string Acronym => "TC";
|
||||||
|
@ -164,28 +164,29 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
ApproachCircle.Expire(true);
|
ApproachCircle.Expire(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void UpdateStartTimeStateTransforms()
|
||||||
|
{
|
||||||
|
base.UpdateStartTimeStateTransforms();
|
||||||
|
|
||||||
|
ApproachCircle.FadeOut(50);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
{
|
{
|
||||||
Debug.Assert(HitObject.HitWindows != null);
|
Debug.Assert(HitObject.HitWindows != null);
|
||||||
|
|
||||||
|
// todo: temporary / arbitrary, used for lifetime optimisation.
|
||||||
|
this.Delay(800).FadeOut();
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
this.Delay(HitObject.TimePreempt).FadeOut(500);
|
|
||||||
HitArea.HitAction = null;
|
HitArea.HitAction = null;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArmedState.Miss:
|
case ArmedState.Miss:
|
||||||
ApproachCircle.FadeOut(50);
|
|
||||||
this.FadeOut(100);
|
this.FadeOut(100);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArmedState.Hit:
|
|
||||||
ApproachCircle.FadeOut(50);
|
|
||||||
|
|
||||||
// todo: temporary / arbitrary
|
|
||||||
this.Delay(800).FadeOut();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Expire();
|
Expire();
|
||||||
|
@ -33,12 +33,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
public SpinnerSpmCounter SpmCounter { get; private set; }
|
public SpinnerSpmCounter SpmCounter { get; private set; }
|
||||||
|
|
||||||
private Container<DrawableSpinnerTick> ticks;
|
private Container<DrawableSpinnerTick> ticks;
|
||||||
private SpinnerBonusDisplay bonusDisplay;
|
|
||||||
private PausableSkinnableSound spinningSample;
|
private PausableSkinnableSound spinningSample;
|
||||||
|
|
||||||
private Bindable<bool> isSpinning;
|
private Bindable<bool> isSpinning;
|
||||||
private bool spinnerFrequencyModulate;
|
private bool spinnerFrequencyModulate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of bonus score gained from spinning after the required number of spins, for display purposes.
|
||||||
|
/// </summary>
|
||||||
|
public IBindable<double> GainedBonus => gainedBonus;
|
||||||
|
|
||||||
|
private readonly Bindable<double> gainedBonus = new Bindable<double>();
|
||||||
|
|
||||||
|
private const double fade_out_duration = 160;
|
||||||
|
|
||||||
public DrawableSpinner()
|
public DrawableSpinner()
|
||||||
: this(null)
|
: this(null)
|
||||||
{
|
{
|
||||||
@ -65,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBody), _ => new DefaultSpinnerDisc()),
|
new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBody), _ => new DefaultSpinner()),
|
||||||
RotationTracker = new SpinnerRotationTracker(this)
|
RotationTracker = new SpinnerRotationTracker(this)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -76,12 +84,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
Y = 120,
|
Y = 120,
|
||||||
Alpha = 0
|
Alpha = 0
|
||||||
},
|
},
|
||||||
bonusDisplay = new SpinnerBonusDisplay
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Y = -120,
|
|
||||||
},
|
|
||||||
spinningSample = new PausableSkinnableSound
|
spinningSample = new PausableSkinnableSound
|
||||||
{
|
{
|
||||||
Volume = { Value = 0 },
|
Volume = { Value = 0 },
|
||||||
@ -131,12 +133,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
if (tracking.NewValue)
|
if (tracking.NewValue)
|
||||||
{
|
{
|
||||||
if (!spinningSample.IsPlaying)
|
if (!spinningSample.IsPlaying)
|
||||||
spinningSample?.Play();
|
spinningSample.Play();
|
||||||
spinningSample?.VolumeTo(1, 300);
|
|
||||||
|
spinningSample.VolumeTo(1, 300);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
spinningSample?.VolumeTo(0, 300).OnComplete(_ => spinningSample.Stop());
|
spinningSample.VolumeTo(0, fade_out_duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,11 +161,29 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void UpdateStartTimeStateTransforms()
|
||||||
|
{
|
||||||
|
base.UpdateStartTimeStateTransforms();
|
||||||
|
|
||||||
|
if (Result?.TimeStarted is double startTime)
|
||||||
|
{
|
||||||
|
using (BeginAbsoluteSequence(startTime))
|
||||||
|
fadeInCounter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
{
|
{
|
||||||
base.UpdateHitStateTransforms(state);
|
base.UpdateHitStateTransforms(state);
|
||||||
|
|
||||||
this.FadeOut(160).Expire();
|
this.FadeOut(fade_out_duration).OnComplete(_ =>
|
||||||
|
{
|
||||||
|
// looping sample should be stopped here as it is safer than running in the OnComplete
|
||||||
|
// of the volume transition above.
|
||||||
|
spinningSample.Stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
Expire();
|
||||||
|
|
||||||
// skin change does a rewind of transforms, which will stop the spinning sound from playing if it's currently in playback.
|
// skin change does a rewind of transforms, which will stop the spinning sound from playing if it's currently in playback.
|
||||||
isSpinning?.TriggerChange();
|
isSpinning?.TriggerChange();
|
||||||
@ -262,13 +283,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
if (!SpmCounter.IsPresent && RotationTracker.Tracking)
|
if (!SpmCounter.IsPresent && RotationTracker.Tracking)
|
||||||
SpmCounter.FadeIn(HitObject.TimeFadeIn);
|
{
|
||||||
|
Result.TimeStarted ??= Time.Current;
|
||||||
|
fadeInCounter();
|
||||||
|
}
|
||||||
|
|
||||||
SpmCounter.SetRotation(Result.RateAdjustedRotation);
|
// don't update after end time to avoid the rate display dropping during fade out.
|
||||||
|
// this shouldn't be limited to StartTime as it causes weirdness with the underlying calculation, which is expecting updates during that period.
|
||||||
|
if (Time.Current <= HitObject.EndTime)
|
||||||
|
SpmCounter.SetRotation(Result.RateAdjustedRotation);
|
||||||
|
|
||||||
updateBonusScore();
|
updateBonusScore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fadeInCounter() => SpmCounter.FadeIn(HitObject.TimeFadeIn);
|
||||||
|
|
||||||
|
private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult;
|
||||||
|
|
||||||
private int wholeSpins;
|
private int wholeSpins;
|
||||||
|
|
||||||
private void updateBonusScore()
|
private void updateBonusScore()
|
||||||
@ -293,8 +324,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
if (tick != null)
|
if (tick != null)
|
||||||
{
|
{
|
||||||
tick.TriggerResult(true);
|
tick.TriggerResult(true);
|
||||||
|
|
||||||
if (tick is DrawableSpinnerBonusTick)
|
if (tick is DrawableSpinnerBonusTick)
|
||||||
bonusDisplay.SetBonusCount(spins - HitObject.SpinsRequired);
|
gainedBonus.Value = score_per_tick * (spins - HitObject.SpinsRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
wholeSpins++;
|
wholeSpins++;
|
||||||
|
@ -29,6 +29,7 @@ using osu.Game.Scoring;
|
|||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
||||||
using osu.Game.Rulesets.Osu.Statistics;
|
using osu.Game.Rulesets.Osu.Statistics;
|
||||||
@ -58,52 +59,52 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
|
|
||||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||||
{
|
{
|
||||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
if (mods.HasFlagFast(LegacyMods.Nightcore))
|
||||||
yield return new OsuModNightcore();
|
yield return new OsuModNightcore();
|
||||||
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
else if (mods.HasFlagFast(LegacyMods.DoubleTime))
|
||||||
yield return new OsuModDoubleTime();
|
yield return new OsuModDoubleTime();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Perfect))
|
if (mods.HasFlagFast(LegacyMods.Perfect))
|
||||||
yield return new OsuModPerfect();
|
yield return new OsuModPerfect();
|
||||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
else if (mods.HasFlagFast(LegacyMods.SuddenDeath))
|
||||||
yield return new OsuModSuddenDeath();
|
yield return new OsuModSuddenDeath();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Autopilot))
|
if (mods.HasFlagFast(LegacyMods.Autopilot))
|
||||||
yield return new OsuModAutopilot();
|
yield return new OsuModAutopilot();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Cinema))
|
if (mods.HasFlagFast(LegacyMods.Cinema))
|
||||||
yield return new OsuModCinema();
|
yield return new OsuModCinema();
|
||||||
else if (mods.HasFlag(LegacyMods.Autoplay))
|
else if (mods.HasFlagFast(LegacyMods.Autoplay))
|
||||||
yield return new OsuModAutoplay();
|
yield return new OsuModAutoplay();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Easy))
|
if (mods.HasFlagFast(LegacyMods.Easy))
|
||||||
yield return new OsuModEasy();
|
yield return new OsuModEasy();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Flashlight))
|
if (mods.HasFlagFast(LegacyMods.Flashlight))
|
||||||
yield return new OsuModFlashlight();
|
yield return new OsuModFlashlight();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.HalfTime))
|
if (mods.HasFlagFast(LegacyMods.HalfTime))
|
||||||
yield return new OsuModHalfTime();
|
yield return new OsuModHalfTime();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.HardRock))
|
if (mods.HasFlagFast(LegacyMods.HardRock))
|
||||||
yield return new OsuModHardRock();
|
yield return new OsuModHardRock();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Hidden))
|
if (mods.HasFlagFast(LegacyMods.Hidden))
|
||||||
yield return new OsuModHidden();
|
yield return new OsuModHidden();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.NoFail))
|
if (mods.HasFlagFast(LegacyMods.NoFail))
|
||||||
yield return new OsuModNoFail();
|
yield return new OsuModNoFail();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Relax))
|
if (mods.HasFlagFast(LegacyMods.Relax))
|
||||||
yield return new OsuModRelax();
|
yield return new OsuModRelax();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.SpunOut))
|
if (mods.HasFlagFast(LegacyMods.SpunOut))
|
||||||
yield return new OsuModSpunOut();
|
yield return new OsuModSpunOut();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Target))
|
if (mods.HasFlagFast(LegacyMods.Target))
|
||||||
yield return new OsuModTarget();
|
yield return new OsuModTarget();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.TouchDevice))
|
if (mods.HasFlagFast(LegacyMods.TouchDevice))
|
||||||
yield return new OsuModTouchDevice();
|
yield return new OsuModTouchDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,6 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
SliderFollowCircle,
|
SliderFollowCircle,
|
||||||
SliderBall,
|
SliderBall,
|
||||||
SliderBody,
|
SliderBody,
|
||||||
SpinnerBody
|
SpinnerBody,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,18 @@
|
|||||||
{
|
{
|
||||||
"Mappings": [{
|
"Mappings": [{
|
||||||
|
"StartTime": 114993,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 114993,
|
||||||
|
"EndTime": 114993,
|
||||||
|
"X": 493,
|
||||||
|
"Y": 92
|
||||||
|
}, {
|
||||||
|
"StartTime": 115290,
|
||||||
|
"EndTime": 115290,
|
||||||
|
"X": 451.659241,
|
||||||
|
"Y": 267.188
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
"StartTime": 118858.0,
|
"StartTime": 118858.0,
|
||||||
"Objects": [{
|
"Objects": [{
|
||||||
"StartTime": 118858.0,
|
"StartTime": 118858.0,
|
||||||
|
@ -9,7 +9,9 @@ SliderMultiplier:1.87
|
|||||||
SliderTickRate:1
|
SliderTickRate:1
|
||||||
|
|
||||||
[TimingPoints]
|
[TimingPoints]
|
||||||
49051,230.769230769231,4,2,1,15,1,0
|
114000,346.820809248555,4,2,1,71,1,0
|
||||||
|
118000,230.769230769231,4,2,1,15,1,0
|
||||||
|
|
||||||
[HitObjects]
|
[HitObjects]
|
||||||
|
493,92,114993,2,0,P|472:181|442:308,1,180,12|0,0:0|0:0,0:0:0:0:
|
||||||
219,215,118858,2,0,P|224:170|244:-10,1,187,8|2,0:0|0:0,0:0:0:0:
|
219,215,118858,2,0,P|224:170|244:-10,1,187,8|2,0:0|0:0,0:0:0:0:
|
||||||
|
68
osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// 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.Globalization;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||||
|
{
|
||||||
|
public class DefaultSpinner : CompositeDrawable
|
||||||
|
{
|
||||||
|
private DrawableSpinner drawableSpinner;
|
||||||
|
|
||||||
|
private OsuSpriteText bonusCounter;
|
||||||
|
|
||||||
|
public DefaultSpinner()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(DrawableHitObject drawableHitObject)
|
||||||
|
{
|
||||||
|
drawableSpinner = (DrawableSpinner)drawableHitObject;
|
||||||
|
|
||||||
|
AddRangeInternal(new Drawable[]
|
||||||
|
{
|
||||||
|
new DefaultSpinnerDisc
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
|
bonusCounter = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Font = OsuFont.Numeric.With(size: 24),
|
||||||
|
Y = -120,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private IBindable<double> gainedBonus;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
gainedBonus = drawableSpinner.GainedBonus.GetBoundCopy();
|
||||||
|
gainedBonus.BindValueChanged(bonus =>
|
||||||
|
{
|
||||||
|
bonusCounter.Text = bonus.NewValue.ToString(NumberFormatInfo.InvariantInfo);
|
||||||
|
bonusCounter.FadeOutFromOne(1500);
|
||||||
|
bonusCounter.ScaleTo(1.5f).Then().ScaleTo(1f, 1000, Easing.OutQuint);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,14 +40,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
public DefaultSpinnerDisc()
|
public DefaultSpinnerDisc()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
// we are slightly bigger than our parent, to clip the top and bottom of the circle
|
// we are slightly bigger than our parent, to clip the top and bottom of the circle
|
||||||
// this should probably be revisited when scaled spinners are a thing.
|
// this should probably be revisited when scaled spinners are a thing.
|
||||||
Scale = new Vector2(initial_scale);
|
Scale = new Vector2(initial_scale);
|
||||||
|
|
||||||
Anchor = Anchor.Centre;
|
|
||||||
Origin = Anchor.Centre;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -74,10 +74,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
private void updateState(DrawableHitObject drawableObject, ArmedState state)
|
private void updateState(DrawableHitObject drawableObject, ArmedState state)
|
||||||
{
|
{
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
|
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
||||||
{
|
|
||||||
glow.FadeOut(400);
|
glow.FadeOut(400);
|
||||||
|
|
||||||
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||||
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
public string Text
|
public string Text
|
||||||
{
|
{
|
||||||
get => number.Text;
|
get => number.Text.ToString();
|
||||||
set => number.Text = value;
|
set => number.Text = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,47 +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.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Shows incremental bonus score achieved for a spinner.
|
|
||||||
/// </summary>
|
|
||||||
public class SpinnerBonusDisplay : CompositeDrawable
|
|
||||||
{
|
|
||||||
private static readonly int score_per_tick = new SpinnerBonusTick().CreateJudgement().MaxNumericResult;
|
|
||||||
|
|
||||||
private readonly OsuSpriteText bonusCounter;
|
|
||||||
|
|
||||||
public SpinnerBonusDisplay()
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
InternalChild = bonusCounter = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Font = OsuFont.Numeric.With(size: 24),
|
|
||||||
Alpha = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private int displayedCount;
|
|
||||||
|
|
||||||
public void SetBonusCount(int count)
|
|
||||||
{
|
|
||||||
if (displayedCount == count)
|
|
||||||
return;
|
|
||||||
|
|
||||||
displayedCount = count;
|
|
||||||
bonusCounter.Text = $"{score_per_tick * count}";
|
|
||||||
bonusCounter.FadeOutFromOne(1500);
|
|
||||||
bonusCounter.ScaleTo(1.5f).Then().ScaleTo(1f, 1000, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -37,9 +37,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
AddInternal(scaleContainer = new Container
|
AddInternal(scaleContainer = new Container
|
||||||
{
|
{
|
||||||
Scale = new Vector2(SPRITE_SCALE),
|
Scale = new Vector2(SPRITE_SCALE),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Y = SPINNER_Y_CENTRE,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
glow = new Sprite
|
glow = new Sprite
|
||||||
|
@ -33,47 +33,38 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
spinnerBlink = source.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.SpinnerNoBlink)?.Value != true;
|
spinnerBlink = source.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.SpinnerNoBlink)?.Value != true;
|
||||||
|
|
||||||
AddInternal(new Container
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
// the old-style spinner relied heavily on absolute screen-space coordinate values.
|
new Sprite
|
||||||
// wrap everything in a container simulating absolute coords to preserve alignment
|
|
||||||
// as there are skins that depend on it.
|
|
||||||
Width = 640,
|
|
||||||
Height = 480,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Sprite
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Texture = source.GetTexture("spinner-background"),
|
||||||
|
Scale = new Vector2(SPRITE_SCALE),
|
||||||
|
Y = SPINNER_Y_CENTRE,
|
||||||
|
},
|
||||||
|
disc = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Texture = source.GetTexture("spinner-circle"),
|
||||||
|
Scale = new Vector2(SPRITE_SCALE),
|
||||||
|
Y = SPINNER_Y_CENTRE,
|
||||||
|
},
|
||||||
|
metre = new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
// this anchor makes no sense, but that's what stable uses.
|
||||||
|
Anchor = Anchor.TopLeft,
|
||||||
|
Origin = Anchor.TopLeft,
|
||||||
|
Margin = new MarginPadding { Top = SPINNER_TOP_OFFSET },
|
||||||
|
Masking = true,
|
||||||
|
Child = metreSprite = new Sprite
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Texture = source.GetTexture("spinner-metre"),
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Texture = source.GetTexture("spinner-background"),
|
|
||||||
Scale = new Vector2(SPRITE_SCALE)
|
|
||||||
},
|
|
||||||
disc = new Sprite
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Texture = source.GetTexture("spinner-circle"),
|
|
||||||
Scale = new Vector2(SPRITE_SCALE)
|
|
||||||
},
|
|
||||||
metre = new Container
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
// this anchor makes no sense, but that's what stable uses.
|
|
||||||
Anchor = Anchor.TopLeft,
|
Anchor = Anchor.TopLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
// adjustment for stable (metre has additional offset)
|
Scale = new Vector2(SPRITE_SCALE)
|
||||||
Margin = new MarginPadding { Top = 20 },
|
|
||||||
Masking = true,
|
|
||||||
Child = metreSprite = new Sprite
|
|
||||||
{
|
|
||||||
Texture = source.GetTexture("spinner-metre"),
|
|
||||||
Anchor = Anchor.TopLeft,
|
|
||||||
Origin = Anchor.TopLeft,
|
|
||||||
Scale = new Vector2(SPRITE_SCALE)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// 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.Globalization;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -16,6 +17,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
public abstract class LegacySpinner : CompositeDrawable
|
public abstract class LegacySpinner : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
/// <remarks>
|
||||||
|
/// All constants are in osu!stable's gamefield space, which is shifted 16px downwards.
|
||||||
|
/// This offset is negated in both osu!stable and osu!lazer to bring all constants into window-space.
|
||||||
|
/// Note: SPINNER_Y_CENTRE + SPINNER_TOP_OFFSET - Position.Y = 240 (=480/2, or half the window-space in osu!stable)
|
||||||
|
/// </remarks>
|
||||||
|
protected const float SPINNER_TOP_OFFSET = 45f - 16f;
|
||||||
|
|
||||||
|
protected const float SPINNER_Y_CENTRE = SPINNER_TOP_OFFSET + 219f;
|
||||||
|
|
||||||
protected const float SPRITE_SCALE = 0.625f;
|
protected const float SPRITE_SCALE = 0.625f;
|
||||||
|
|
||||||
protected DrawableSpinner DrawableSpinner { get; private set; }
|
protected DrawableSpinner DrawableSpinner { get; private set; }
|
||||||
@ -23,43 +33,82 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
private Sprite spin;
|
private Sprite spin;
|
||||||
private Sprite clear;
|
private Sprite clear;
|
||||||
|
|
||||||
|
private LegacySpriteText bonusCounter;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(DrawableHitObject drawableHitObject, ISkinSource source)
|
private void load(DrawableHitObject drawableHitObject, ISkinSource source)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
// osu!stable positions spinner components in window-space (as opposed to gamefield-space). This is a 640x480 area taking up the entire screen.
|
||||||
|
// In lazer, the gamefield-space positional transformation is applied in OsuPlayfieldAdjustmentContainer, which is inverted here to make this area take up the entire window space.
|
||||||
|
Size = new Vector2(640, 480);
|
||||||
|
Position = new Vector2(0, -8f);
|
||||||
|
|
||||||
DrawableSpinner = (DrawableSpinner)drawableHitObject;
|
DrawableSpinner = (DrawableSpinner)drawableHitObject;
|
||||||
|
|
||||||
AddRangeInternal(new[]
|
Container overlayContainer;
|
||||||
|
|
||||||
|
AddInternal(overlayContainer = new Container
|
||||||
{
|
{
|
||||||
spin = new Sprite
|
Depth = float.MinValue,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
spin = new Sprite
|
||||||
Origin = Anchor.Centre,
|
{
|
||||||
Depth = float.MinValue,
|
Anchor = Anchor.TopCentre,
|
||||||
Texture = source.GetTexture("spinner-spin"),
|
Origin = Anchor.Centre,
|
||||||
Scale = new Vector2(SPRITE_SCALE),
|
Texture = source.GetTexture("spinner-spin"),
|
||||||
Y = 120 - 45 // offset temporarily to avoid overlapping default spin counter
|
Scale = new Vector2(SPRITE_SCALE),
|
||||||
},
|
Y = SPINNER_TOP_OFFSET + 335,
|
||||||
clear = new Sprite
|
},
|
||||||
{
|
clear = new Sprite
|
||||||
Anchor = Anchor.Centre,
|
{
|
||||||
Origin = Anchor.Centre,
|
Alpha = 0,
|
||||||
Depth = float.MinValue,
|
Anchor = Anchor.TopCentre,
|
||||||
Alpha = 0,
|
Origin = Anchor.Centre,
|
||||||
Texture = source.GetTexture("spinner-clear"),
|
Texture = source.GetTexture("spinner-clear"),
|
||||||
Scale = new Vector2(SPRITE_SCALE),
|
Scale = new Vector2(SPRITE_SCALE),
|
||||||
Y = -60
|
Y = SPINNER_TOP_OFFSET + 115,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bonusCounter = (source.GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreText)) as LegacySpriteText)?.With(c =>
|
||||||
|
{
|
||||||
|
c.Alpha = 0f;
|
||||||
|
c.Anchor = Anchor.TopCentre;
|
||||||
|
c.Origin = Anchor.Centre;
|
||||||
|
c.Font = c.Font.With(fixedWidth: false);
|
||||||
|
c.Scale = new Vector2(SPRITE_SCALE);
|
||||||
|
c.Y = SPINNER_TOP_OFFSET + 299;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (bonusCounter != null)
|
||||||
|
overlayContainer.Add(bonusCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IBindable<double> gainedBonus;
|
||||||
|
|
||||||
private readonly Bindable<bool> completed = new Bindable<bool>();
|
private readonly Bindable<bool> completed = new Bindable<bool>();
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (bonusCounter != null)
|
||||||
|
{
|
||||||
|
gainedBonus = DrawableSpinner.GainedBonus.GetBoundCopy();
|
||||||
|
gainedBonus.BindValueChanged(bonus =>
|
||||||
|
{
|
||||||
|
bonusCounter.Text = bonus.NewValue.ToString(NumberFormatInfo.InvariantInfo);
|
||||||
|
bonusCounter.FadeOutFromOne(800, Easing.Out);
|
||||||
|
bonusCounter.ScaleTo(SPRITE_SCALE * 2f).Then().ScaleTo(SPRITE_SCALE * 1.28f, 800, Easing.Out);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
completed.BindValueChanged(onCompletedChanged, true);
|
completed.BindValueChanged(onCompletedChanged, true);
|
||||||
|
|
||||||
DrawableSpinner.ApplyCustomUpdateState += UpdateStateTransforms;
|
DrawableSpinner.ApplyCustomUpdateState += UpdateStateTransforms;
|
||||||
|
@ -2,8 +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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
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.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.Taiko.UI;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
@ -13,6 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneHitExplosion : TaikoSkinnableTestScene
|
public class TestSceneHitExplosion : TaikoSkinnableTestScene
|
||||||
{
|
{
|
||||||
|
protected override double TimePerAction => 100;
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestNormalHit()
|
public void TestNormalHit()
|
||||||
{
|
{
|
||||||
@ -21,11 +27,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
|
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[TestCase(HitResult.Great)]
|
||||||
public void TestStrongHit([Values(false, true)] bool hitBoth)
|
[TestCase(HitResult.Ok)]
|
||||||
|
public void TestStrongHit(HitResult type)
|
||||||
{
|
{
|
||||||
AddStep("Great", () => SetContents(() => getContentFor(createStrongHit(HitResult.Great, hitBoth))));
|
AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type))));
|
||||||
AddStep("Good", () => SetContents(() => getContentFor(createStrongHit(HitResult.Ok, hitBoth))));
|
AddStep("visualise second hit",
|
||||||
|
() => this.ChildrenOfType<HitExplosion>()
|
||||||
|
.ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable getContentFor(DrawableTestHit hit)
|
private Drawable getContentFor(DrawableTestHit hit)
|
||||||
@ -38,17 +47,17 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
// the hit needs to be added to hierarchy in order for nested objects to be created correctly.
|
// the hit needs to be added to hierarchy in order for nested objects to be created correctly.
|
||||||
// setting zero alpha is supposed to prevent the test from looking broken.
|
// setting zero alpha is supposed to prevent the test from looking broken.
|
||||||
hit.With(h => h.Alpha = 0),
|
hit.With(h => h.Alpha = 0),
|
||||||
new HitExplosion(hit, hit.Type)
|
new HitExplosion(hit.Type)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}
|
}.With(explosion => explosion.Apply(hit))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private DrawableTestHit createHit(HitResult type) => new DrawableTestHit(new Hit { StartTime = Time.Current }, type);
|
private DrawableTestHit createHit(HitResult type) => new DrawableTestHit(new Hit { StartTime = Time.Current }, type);
|
||||||
|
|
||||||
private DrawableTestHit createStrongHit(HitResult type, bool hitBoth) => new DrawableTestStrongHit(Time.Current, type, hitBoth);
|
private DrawableTestHit createStrongHit(HitResult type) => new DrawableTestStrongHit(Time.Current, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ 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;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Judgements;
|
using osu.Game.Rulesets.Taiko.Judgements;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -108,12 +107,12 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great;
|
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great;
|
||||||
|
|
||||||
Hit hit = new Hit();
|
Hit hit = new Hit { StartTime = DrawableRuleset.Playfield.Time.Current };
|
||||||
var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) };
|
var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) };
|
||||||
|
|
||||||
DrawableRuleset.Playfield.Add(h);
|
DrawableRuleset.Playfield.Add(h);
|
||||||
|
|
||||||
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStrongHitJudgement(bool kiai)
|
private void addStrongHitJudgement(bool kiai)
|
||||||
@ -122,6 +121,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
Hit hit = new Hit
|
Hit hit = new Hit
|
||||||
{
|
{
|
||||||
|
StartTime = DrawableRuleset.Playfield.Time.Current,
|
||||||
IsStrong = true,
|
IsStrong = true,
|
||||||
Samples = createSamples(strong: true)
|
Samples = createSamples(strong: true)
|
||||||
};
|
};
|
||||||
@ -129,8 +129,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
DrawableRuleset.Playfield.Add(h);
|
DrawableRuleset.Playfield.Add(h);
|
||||||
|
|
||||||
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult });
|
||||||
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great });
|
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(hit.NestedHitObjects.Single(), new TaikoStrongJudgement()) { Type = HitResult.Great });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMissJudgement()
|
private void addMissJudgement()
|
||||||
|
@ -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.8.3" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Difficulty.Utils;
|
using osu.Game.Rulesets.Difficulty.Utils;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
@ -39,6 +40,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private int currentMonoLength;
|
private int currentMonoLength;
|
||||||
|
|
||||||
|
public Colour(Mod[] mods)
|
||||||
|
: base(mods)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
{
|
{
|
||||||
// changing from/to a drum roll or a swell does not constitute a colour change.
|
// changing from/to a drum roll or a swell does not constitute a colour change.
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Difficulty.Utils;
|
using osu.Game.Rulesets.Difficulty.Utils;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
@ -47,6 +48,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private int notesSinceRhythmChange;
|
private int notesSinceRhythmChange;
|
||||||
|
|
||||||
|
public Rhythm(Mod[] mods)
|
||||||
|
: base(mods)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
{
|
{
|
||||||
// drum rolls and swells are exempt.
|
// drum rolls and swells are exempt.
|
||||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Difficulty.Utils;
|
using osu.Game.Rulesets.Difficulty.Utils;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
@ -48,8 +49,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="Stamina"/> skill.
|
/// Creates a <see cref="Stamina"/> skill.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="mods">Mods for use in skill calculations.</param>
|
||||||
/// <param name="rightHand">Whether this instance is performing calculations for the right hand.</param>
|
/// <param name="rightHand">Whether this instance is performing calculations for the right hand.</param>
|
||||||
public Stamina(bool rightHand)
|
public Stamina(Mod[] mods, bool rightHand)
|
||||||
|
: base(mods)
|
||||||
{
|
{
|
||||||
hand = rightHand ? 1 : 0;
|
hand = rightHand ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
||||||
{
|
{
|
||||||
new Colour(),
|
new Colour(mods),
|
||||||
new Rhythm(),
|
new Rhythm(mods),
|
||||||
new Stamina(true),
|
new Stamina(mods, true),
|
||||||
new Stamina(false),
|
new Stamina(mods, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||||
|
@ -52,32 +52,24 @@ namespace osu.Game.Rulesets.Taiko.Edit
|
|||||||
|
|
||||||
public void SetStrongState(bool state)
|
public void SetStrongState(bool state)
|
||||||
{
|
{
|
||||||
var hits = EditorBeatmap.SelectedHitObjects.OfType<Hit>();
|
EditorBeatmap.PerformOnSelection(h =>
|
||||||
|
|
||||||
EditorBeatmap.BeginChange();
|
|
||||||
|
|
||||||
foreach (var h in hits)
|
|
||||||
{
|
{
|
||||||
if (h.IsStrong != state)
|
if (!(h is Hit taikoHit)) return;
|
||||||
{
|
|
||||||
h.IsStrong = state;
|
|
||||||
EditorBeatmap.Update(h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorBeatmap.EndChange();
|
if (taikoHit.IsStrong != state)
|
||||||
|
{
|
||||||
|
taikoHit.IsStrong = state;
|
||||||
|
EditorBeatmap.Update(taikoHit);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRimState(bool state)
|
public void SetRimState(bool state)
|
||||||
{
|
{
|
||||||
var hits = EditorBeatmap.SelectedHitObjects.OfType<Hit>();
|
EditorBeatmap.PerformOnSelection(h =>
|
||||||
|
{
|
||||||
EditorBeatmap.BeginChange();
|
if (h is Hit taikoHit) taikoHit.Type = state ? HitType.Rim : HitType.Centre;
|
||||||
|
});
|
||||||
foreach (var h in hits)
|
|
||||||
h.Type = state ? HitType.Rim : HitType.Centre;
|
|
||||||
|
|
||||||
EditorBeatmap.EndChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumerable<SelectionBlueprint> selection)
|
protected override IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumerable<SelectionBlueprint> selection)
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
// 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 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.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||||
{
|
{
|
||||||
public class LegacyHitExplosion : CompositeDrawable
|
public class LegacyHitExplosion : CompositeDrawable, IAnimatableHitExplosion
|
||||||
{
|
{
|
||||||
private readonly Drawable sprite;
|
private readonly Drawable sprite;
|
||||||
private readonly Drawable strongSprite;
|
|
||||||
|
|
||||||
private DrawableStrongNestedHit nestedStrongHit;
|
[CanBeNull]
|
||||||
private bool switchedToStrongSprite;
|
private readonly Drawable strongSprite;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new legacy hit explosion.
|
/// Creates a new legacy hit explosion.
|
||||||
@ -27,14 +27,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="sprite">The normal legacy explosion sprite.</param>
|
/// <param name="sprite">The normal legacy explosion sprite.</param>
|
||||||
/// <param name="strongSprite">The strong legacy explosion sprite.</param>
|
/// <param name="strongSprite">The strong legacy explosion sprite.</param>
|
||||||
public LegacyHitExplosion(Drawable sprite, Drawable strongSprite = null)
|
public LegacyHitExplosion(Drawable sprite, [CanBeNull] Drawable strongSprite = null)
|
||||||
{
|
{
|
||||||
this.sprite = sprite;
|
this.sprite = sprite;
|
||||||
this.strongSprite = strongSprite;
|
this.strongSprite = strongSprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(DrawableHitObject judgedObject)
|
private void load()
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
@ -56,45 +56,30 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
s.Origin = Anchor.Centre;
|
s.Origin = Anchor.Centre;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (judgedObject is DrawableHit hit)
|
|
||||||
nestedStrongHit = hit.NestedHitObjects.SingleOrDefault() as DrawableStrongNestedHit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
public void Animate(DrawableHitObject drawableHitObject)
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
const double animation_time = 120;
|
const double animation_time = 120;
|
||||||
|
|
||||||
|
(sprite as IFramedAnimation)?.GotoFrame(0);
|
||||||
|
(strongSprite as IFramedAnimation)?.GotoFrame(0);
|
||||||
|
|
||||||
this.FadeInFromZero(animation_time).Then().FadeOut(animation_time * 1.5);
|
this.FadeInFromZero(animation_time).Then().FadeOut(animation_time * 1.5);
|
||||||
|
|
||||||
this.ScaleTo(0.6f)
|
this.ScaleTo(0.6f)
|
||||||
.Then().ScaleTo(1.1f, animation_time * 0.8)
|
.Then().ScaleTo(1.1f, animation_time * 0.8)
|
||||||
.Then().ScaleTo(0.9f, animation_time * 0.4)
|
.Then().ScaleTo(0.9f, animation_time * 0.4)
|
||||||
.Then().ScaleTo(1f, animation_time * 0.2);
|
.Then().ScaleTo(1f, animation_time * 0.2);
|
||||||
|
|
||||||
Expire(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
public void AnimateSecondHit()
|
||||||
{
|
{
|
||||||
base.Update();
|
if (strongSprite == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (shouldSwitchToStrongSprite() && !switchedToStrongSprite)
|
sprite.FadeOut(50, Easing.OutQuint);
|
||||||
{
|
strongSprite.FadeIn(50, Easing.OutQuint);
|
||||||
sprite.FadeOut(50, Easing.OutQuint);
|
|
||||||
strongSprite.FadeIn(50, Easing.OutQuint);
|
|
||||||
switchedToStrongSprite = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool shouldSwitchToStrongSprite()
|
|
||||||
{
|
|
||||||
if (nestedStrongHit == null || strongSprite == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return nestedStrongHit.IsHit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
// if a taiko skin is providing explosion sprites, hide the judgements completely
|
// if a taiko skin is providing explosion sprites, hide the judgements completely
|
||||||
if (hasExplosion.Value)
|
if (hasExplosion.Value)
|
||||||
return Drawable.Empty();
|
return Drawable.Empty().With(d => d.Expire());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(component is TaikoSkinComponent taikoComponent))
|
if (!(component is TaikoSkinComponent taikoComponent))
|
||||||
@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
// suppress the default kiai explosion if the skin brings its own sprites.
|
// suppress the default kiai explosion if the skin brings its own sprites.
|
||||||
// the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield.
|
// the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield.
|
||||||
if (hasExplosion.Value)
|
if (hasExplosion.Value)
|
||||||
return Drawable.Empty().With(d => d.LifetimeEnd = double.MinValue);
|
return Drawable.Empty().With(d => d.Expire());
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}");
|
throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Sample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));
|
public override ISample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));
|
||||||
|
|
||||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => Source.GetConfig<TLookup, TValue>(lookup);
|
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => Source.GetConfig<TLookup, TValue>(lookup);
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ using osu.Game.Rulesets.Taiko.Scoring;
|
|||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Taiko.Edit;
|
using osu.Game.Rulesets.Taiko.Edit;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -57,43 +58,43 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
|
|
||||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||||
{
|
{
|
||||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
if (mods.HasFlagFast(LegacyMods.Nightcore))
|
||||||
yield return new TaikoModNightcore();
|
yield return new TaikoModNightcore();
|
||||||
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
else if (mods.HasFlagFast(LegacyMods.DoubleTime))
|
||||||
yield return new TaikoModDoubleTime();
|
yield return new TaikoModDoubleTime();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Perfect))
|
if (mods.HasFlagFast(LegacyMods.Perfect))
|
||||||
yield return new TaikoModPerfect();
|
yield return new TaikoModPerfect();
|
||||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
else if (mods.HasFlagFast(LegacyMods.SuddenDeath))
|
||||||
yield return new TaikoModSuddenDeath();
|
yield return new TaikoModSuddenDeath();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Cinema))
|
if (mods.HasFlagFast(LegacyMods.Cinema))
|
||||||
yield return new TaikoModCinema();
|
yield return new TaikoModCinema();
|
||||||
else if (mods.HasFlag(LegacyMods.Autoplay))
|
else if (mods.HasFlagFast(LegacyMods.Autoplay))
|
||||||
yield return new TaikoModAutoplay();
|
yield return new TaikoModAutoplay();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Easy))
|
if (mods.HasFlagFast(LegacyMods.Easy))
|
||||||
yield return new TaikoModEasy();
|
yield return new TaikoModEasy();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Flashlight))
|
if (mods.HasFlagFast(LegacyMods.Flashlight))
|
||||||
yield return new TaikoModFlashlight();
|
yield return new TaikoModFlashlight();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.HalfTime))
|
if (mods.HasFlagFast(LegacyMods.HalfTime))
|
||||||
yield return new TaikoModHalfTime();
|
yield return new TaikoModHalfTime();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.HardRock))
|
if (mods.HasFlagFast(LegacyMods.HardRock))
|
||||||
yield return new TaikoModHardRock();
|
yield return new TaikoModHardRock();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Hidden))
|
if (mods.HasFlagFast(LegacyMods.Hidden))
|
||||||
yield return new TaikoModHidden();
|
yield return new TaikoModHidden();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.NoFail))
|
if (mods.HasFlagFast(LegacyMods.NoFail))
|
||||||
yield return new TaikoModNoFail();
|
yield return new TaikoModNoFail();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Relax))
|
if (mods.HasFlagFast(LegacyMods.Relax))
|
||||||
yield return new TaikoModRelax();
|
yield return new TaikoModRelax();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Random))
|
if (mods.HasFlagFast(LegacyMods.Random))
|
||||||
yield return new TaikoModRandom();
|
yield return new TaikoModRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using JetBrains.Annotations;
|
||||||
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;
|
||||||
@ -13,19 +14,23 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.UI
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
{
|
{
|
||||||
internal class DefaultHitExplosion : CircularContainer
|
internal class DefaultHitExplosion : CircularContainer, IAnimatableHitExplosion
|
||||||
{
|
{
|
||||||
private readonly DrawableHitObject judgedObject;
|
|
||||||
private readonly HitResult result;
|
private readonly HitResult result;
|
||||||
|
|
||||||
public DefaultHitExplosion(DrawableHitObject judgedObject, HitResult result)
|
[CanBeNull]
|
||||||
|
private Box body;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
|
public DefaultHitExplosion(HitResult result)
|
||||||
{
|
{
|
||||||
this.judgedObject = judgedObject;
|
|
||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
@ -40,26 +45,36 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
if (!result.IsHit())
|
if (!result.IsHit())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
|
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
new Box
|
body = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = isRim ? colours.BlueDarker : colours.PinkDarker,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
updateColour();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
private void updateColour([CanBeNull] DrawableHitObject judgedObject = null)
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
if (body == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool isRim = (judgedObject?.HitObject as Hit)?.Type == HitType.Rim;
|
||||||
|
body.Colour = isRim ? colours.BlueDarker : colours.PinkDarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Animate(DrawableHitObject drawableHitObject)
|
||||||
|
{
|
||||||
|
updateColour(drawableHitObject);
|
||||||
|
|
||||||
this.ScaleTo(3f, 1000, Easing.OutQuint);
|
this.ScaleTo(3f, 1000, Easing.OutQuint);
|
||||||
this.FadeOut(500);
|
this.FadeOut(500);
|
||||||
|
}
|
||||||
|
|
||||||
Expire(true);
|
public void AnimateSecondHit()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.UI
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
{
|
{
|
||||||
@ -12,16 +11,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DrawableTaikoJudgement : DrawableJudgement
|
public class DrawableTaikoJudgement : DrawableJudgement
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Creates a new judgement text.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="judgedObject">The object which is being judged.</param>
|
|
||||||
/// <param name="result">The judgement to visualise.</param>
|
|
||||||
public DrawableTaikoJudgement(JudgementResult result, DrawableHitObject judgedObject)
|
|
||||||
: base(result, judgedObject)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ApplyHitAnimations()
|
protected override void ApplyHitAnimations()
|
||||||
{
|
{
|
||||||
this.MoveToY(-100, 500);
|
this.MoveToY(-100, 500);
|
||||||
|
@ -2,10 +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 JetBrains.Annotations;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
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.Pooling;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -16,31 +18,37 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A circle explodes from the hit target to indicate a hitobject has been hit.
|
/// A circle explodes from the hit target to indicate a hitobject has been hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class HitExplosion : CircularContainer
|
internal class HitExplosion : PoolableDrawable
|
||||||
{
|
{
|
||||||
public override bool RemoveWhenNotAlive => true;
|
public override bool RemoveWhenNotAlive => true;
|
||||||
|
public override bool RemoveCompletedTransforms => false;
|
||||||
[Cached(typeof(DrawableHitObject))]
|
|
||||||
public readonly DrawableHitObject JudgedObject;
|
|
||||||
|
|
||||||
private readonly HitResult result;
|
private readonly HitResult result;
|
||||||
|
|
||||||
|
private double? secondHitTime;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
public DrawableHitObject JudgedObject;
|
||||||
|
|
||||||
private SkinnableDrawable skinnable;
|
private SkinnableDrawable skinnable;
|
||||||
|
|
||||||
public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
|
/// <summary>
|
||||||
|
/// This constructor only exists to meet the <c>new()</c> type constraint of <see cref="DrawablePool{T}"/>.
|
||||||
public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
|
/// </summary>
|
||||||
|
public HitExplosion()
|
||||||
public HitExplosion(DrawableHitObject judgedObject, HitResult result)
|
: this(HitResult.Great)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public HitExplosion(HitResult result)
|
||||||
{
|
{
|
||||||
JudgedObject = judgedObject;
|
|
||||||
this.result = result;
|
this.result = result;
|
||||||
|
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE);
|
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE);
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
RelativePositionAxes = Axes.Both;
|
RelativePositionAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
@ -48,7 +56,47 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(JudgedObject, result));
|
InternalChild = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(result));
|
||||||
|
skinnable.OnSkinChanged += runAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply([CanBeNull] DrawableHitObject drawableHitObject)
|
||||||
|
{
|
||||||
|
JudgedObject = drawableHitObject;
|
||||||
|
secondHitTime = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PrepareForUse()
|
||||||
|
{
|
||||||
|
base.PrepareForUse();
|
||||||
|
runAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runAnimation()
|
||||||
|
{
|
||||||
|
if (JudgedObject?.Result == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double resultTime = JudgedObject.Result.TimeAbsolute;
|
||||||
|
|
||||||
|
LifetimeStart = resultTime;
|
||||||
|
|
||||||
|
ApplyTransformsAt(double.MinValue, true);
|
||||||
|
ClearTransforms(true);
|
||||||
|
|
||||||
|
using (BeginAbsoluteSequence(resultTime))
|
||||||
|
(skinnable.Drawable as IAnimatableHitExplosion)?.Animate(JudgedObject);
|
||||||
|
|
||||||
|
if (secondHitTime != null)
|
||||||
|
{
|
||||||
|
using (BeginAbsoluteSequence(secondHitTime.Value))
|
||||||
|
{
|
||||||
|
this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50);
|
||||||
|
(skinnable.Drawable as IAnimatableHitExplosion)?.AnimateSecondHit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LifetimeEnd = skinnable.Drawable.LatestTransformEndTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TaikoSkinComponents getComponentName(HitResult result)
|
private static TaikoSkinComponents getComponentName(HitResult result)
|
||||||
@ -68,12 +116,10 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}");
|
throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public void VisualiseSecondHit(JudgementResult judgementResult)
|
||||||
/// Transforms this hit explosion to visualise a secondary hit.
|
|
||||||
/// </summary>
|
|
||||||
public void VisualiseSecondHit()
|
|
||||||
{
|
{
|
||||||
this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50);
|
secondHitTime = judgementResult.TimeAbsolute;
|
||||||
|
runAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics.Pooling;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pool for hit explosions of a specific type.
|
||||||
|
/// </summary>
|
||||||
|
internal class HitExplosionPool : DrawablePool<HitExplosion>
|
||||||
|
{
|
||||||
|
private readonly HitResult hitResult;
|
||||||
|
|
||||||
|
public HitExplosionPool(HitResult hitResult)
|
||||||
|
: base(15)
|
||||||
|
{
|
||||||
|
this.hitResult = hitResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override HitExplosion CreateNewDrawable() => new HitExplosion(hitResult);
|
||||||
|
}
|
||||||
|
}
|
23
osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A skinnable element of a hit explosion that supports playing an animation from the current point in time.
|
||||||
|
/// </summary>
|
||||||
|
public interface IAnimatableHitExplosion
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the hit explosion for the supplied <paramref name="drawableHitObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
void Animate(DrawableHitObject drawableHitObject);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transforms the hit explosion to visualise a secondary hit.
|
||||||
|
/// </summary>
|
||||||
|
void AnimateSecondHit();
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +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 System.Linq;
|
||||||
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.Framework.Graphics.Pooling;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -17,6 +19,7 @@ using osu.Game.Rulesets.UI.Scrolling;
|
|||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Judgements;
|
using osu.Game.Rulesets.Taiko.Judgements;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -38,6 +41,9 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
internal Drawable HitTarget;
|
internal Drawable HitTarget;
|
||||||
private SkinnableDrawable mascot;
|
private SkinnableDrawable mascot;
|
||||||
|
|
||||||
|
private readonly IDictionary<HitResult, DrawablePool<DrawableTaikoJudgement>> judgementPools = new Dictionary<HitResult, DrawablePool<DrawableTaikoJudgement>>();
|
||||||
|
private readonly IDictionary<HitResult, HitExplosionPool> explosionPools = new Dictionary<HitResult, HitExplosionPool>();
|
||||||
|
|
||||||
private ProxyContainer topLevelHitContainer;
|
private ProxyContainer topLevelHitContainer;
|
||||||
private Container rightArea;
|
private Container rightArea;
|
||||||
private Container leftArea;
|
private Container leftArea;
|
||||||
@ -159,6 +165,17 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
RegisterPool<Swell, DrawableSwell>(5);
|
RegisterPool<Swell, DrawableSwell>(5);
|
||||||
RegisterPool<SwellTick, DrawableSwellTick>(100);
|
RegisterPool<SwellTick, DrawableSwellTick>(100);
|
||||||
|
|
||||||
|
var hitWindows = new TaikoHitWindows();
|
||||||
|
|
||||||
|
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => hitWindows.IsHitResultAllowed(r)))
|
||||||
|
{
|
||||||
|
judgementPools.Add(result, new DrawablePool<DrawableTaikoJudgement>(15));
|
||||||
|
explosionPools.Add(result, new HitExplosionPool(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
AddRangeInternal(judgementPools.Values);
|
||||||
|
AddRangeInternal(explosionPools.Values);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -270,7 +287,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
case TaikoStrongJudgement _:
|
case TaikoStrongJudgement _:
|
||||||
if (result.IsHit)
|
if (result.IsHit)
|
||||||
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit();
|
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(result);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TaikoDrumRollTickJudgement _:
|
case TaikoDrumRollTickJudgement _:
|
||||||
@ -283,13 +300,15 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject)
|
judgementContainer.Add(judgementPools[result.Type].Get(j =>
|
||||||
{
|
{
|
||||||
Anchor = result.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
|
j.Apply(result, judgedObject);
|
||||||
Origin = result.IsHit ? Anchor.BottomCentre : Anchor.Centre,
|
|
||||||
RelativePositionAxes = Axes.X,
|
j.Anchor = result.IsHit ? Anchor.TopLeft : Anchor.CentreLeft;
|
||||||
X = result.IsHit ? judgedObject.Position.X : 0,
|
j.Origin = result.IsHit ? Anchor.BottomCentre : Anchor.Centre;
|
||||||
});
|
j.RelativePositionAxes = Axes.X;
|
||||||
|
j.X = result.IsHit ? judgedObject.Position.X : 0;
|
||||||
|
}));
|
||||||
|
|
||||||
var type = (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre;
|
var type = (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre;
|
||||||
addExplosion(judgedObject, result.Type, type);
|
addExplosion(judgedObject, result.Type, type);
|
||||||
@ -302,7 +321,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type)
|
private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type)
|
||||||
{
|
{
|
||||||
hitExplosionContainer.Add(new HitExplosion(drawableObject, result));
|
hitExplosionContainer.Add(explosionPools[result]
|
||||||
|
.Get(explosion => explosion.Apply(drawableObject)));
|
||||||
if (drawableObject.HitObject.Kiai)
|
if (drawableObject.HitObject.Kiai)
|
||||||
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
|
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Properties\AndroidManifest.xml" />
|
<None Include="Properties\AndroidManifest.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);CA2007</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\osu.Game.Tests\**\Beatmaps\**\*.cs">
|
<Compile Include="..\osu.Game.Tests\**\Beatmaps\**\*.cs">
|
||||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||||
@ -71,7 +74,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="DeepEqual" Version="2.0.0" />
|
<PackageReference Include="DeepEqual" Version="2.0.0" />
|
||||||
<PackageReference Include="Moq" Version="4.16.0" />
|
<PackageReference Include="Moq" Version="4.16.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
|