mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:07:52 +08:00
Merge branch 'master' into fix-unsafe-skinnable-sample-play
This commit is contained in:
commit
aec859b4d1
@ -194,4 +194,7 @@ dotnet_diagnostic.IDE0068.severity = none
|
||||
dotnet_diagnostic.IDE0069.severity = none
|
||||
|
||||
#Disable operator overloads requiring alternate named methods
|
||||
dotnet_diagnostic.CA2225.severity = none
|
||||
dotnet_diagnostic.CA2225.severity = none
|
||||
|
||||
# Banned APIs
|
||||
dotnet_diagnostic.RS0030.severity = error
|
2
.github/ISSUE_TEMPLATE/01-bug-issues.md
vendored
2
.github/ISSUE_TEMPLATE/01-bug-issues.md
vendored
@ -13,4 +13,6 @@ about: Issues regarding encountered bugs.
|
||||
*please attach logs here, which are located at:*
|
||||
- `%AppData%/osu/logs` *(on Windows),*
|
||||
- `~/.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)
|
||||
-->
|
||||
|
2
.github/ISSUE_TEMPLATE/02-crash-issues.md
vendored
2
.github/ISSUE_TEMPLATE/02-crash-issues.md
vendored
@ -13,6 +13,8 @@ about: Issues regarding crashes or permanent freezes.
|
||||
*please attach logs here, which are located at:*
|
||||
- `%AppData%/osu/logs` *(on Windows),*
|
||||
- `~/.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)
|
||||
-->
|
||||
|
||||
**Computer Specifications:**
|
||||
|
@ -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)
|
||||
T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;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="CA1822" Action="None" />
|
||||
<Rule Id="CA1823" Action="None" />
|
||||
<Rule Id="CA2007" Action="None" />
|
||||
<Rule Id="CA2007" Action="Warning" />
|
||||
<Rule Id="CA2214" Action="None" />
|
||||
<Rule Id="CA2227" Action="None" />
|
||||
</Rules>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<ItemGroup Label="Code Analysis">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.2" PrivateAssets="All" />
|
||||
<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>
|
||||
<PropertyGroup Label="Code Analysis">
|
||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
||||
|
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.222.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.317.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -100,15 +100,15 @@ namespace osu.Android
|
||||
// copy to an arbitrary-access memory stream to be able to proceed with the import.
|
||||
var copy = new MemoryStream();
|
||||
using (var stream = ContentResolver.OpenInputStream(uri))
|
||||
await stream.CopyToAsync(copy);
|
||||
await stream.CopyToAsync(copy).ConfigureAwait(false);
|
||||
|
||||
lock (tasks)
|
||||
{
|
||||
tasks.Add(new ImportTask(copy, filename));
|
||||
}
|
||||
}));
|
||||
})).ConfigureAwait(false);
|
||||
|
||||
await game.Import(tasks.ToArray());
|
||||
await game.Import(tasks.ToArray()).ConfigureAwait(false);
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
|
@ -136,24 +136,12 @@ namespace osu.Desktop
|
||||
|
||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||
|
||||
switch (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;
|
||||
var desktopWindow = (SDL2DesktopWindow)host.Window;
|
||||
|
||||
// SDL2 DesktopWindow
|
||||
case SDL2DesktopWindow desktopWindow:
|
||||
desktopWindow.CursorState |= CursorState.Hidden;
|
||||
desktopWindow.SetIconFromStream(iconStream);
|
||||
desktopWindow.Title = Name;
|
||||
desktopWindow.DragDrop += f => fileDrop(new[] { f });
|
||||
break;
|
||||
}
|
||||
desktopWindow.CursorState |= CursorState.Hidden;
|
||||
desktopWindow.SetIconFromStream(iconStream);
|
||||
desktopWindow.Title = Name;
|
||||
desktopWindow.DragDrop += f => fileDrop(new[] { f });
|
||||
}
|
||||
|
||||
private void fileDrop(string[] filePaths)
|
||||
|
@ -22,9 +22,8 @@ namespace osu.Desktop
|
||||
{
|
||||
// Back up the cwd before DesktopGameHost changes it
|
||||
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;
|
||||
|
||||
|
@ -42,7 +42,7 @@ namespace osu.Desktop.Updater
|
||||
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)
|
||||
{
|
||||
@ -51,9 +51,9 @@ namespace osu.Desktop.Updater
|
||||
|
||||
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)
|
||||
{
|
||||
@ -79,12 +79,12 @@ namespace osu.Desktop.Updater
|
||||
|
||||
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.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;
|
||||
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)
|
||||
// try again without deltas.
|
||||
await checkForUpdateAsync(false, notification);
|
||||
await checkForUpdateAsync(false, notification).ConfigureAwait(false);
|
||||
scheduleRecheck = false;
|
||||
}
|
||||
else
|
||||
@ -116,7 +116,7 @@ namespace osu.Desktop.Updater
|
||||
if (scheduleRecheck)
|
||||
{
|
||||
// check again in 30 minutes.
|
||||
Scheduler.AddDelayed(async () => await checkForUpdateAsync(), 60000 * 30);
|
||||
Scheduler.AddDelayed(async () => await checkForUpdateAsync().ConfigureAwait(false), 60000 * 30);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@
|
||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="5.0.0" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
||||
</ItemGroup>
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
LocalConfig.Set(OsuSetting.IncreaseFirstObjectVisibility, false);
|
||||
LocalConfig.SetValue(OsuSetting.IncreaseFirstObjectVisibility, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -202,7 +202,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
public void TestHitLightingColour()
|
||||
{
|
||||
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()));
|
||||
AddAssert("correct hit lighting colour", () =>
|
||||
catcher.ChildrenOfType<HitExplosion>().First()?.ObjectColour == fruitColour);
|
||||
@ -211,7 +211,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[Test]
|
||||
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()));
|
||||
AddAssert("no hit lighting", () => !catcher.ChildrenOfType<HitExplosion>().Any());
|
||||
}
|
||||
|
@ -2,10 +2,10 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
@ -21,6 +21,7 @@ using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using System;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Legacy;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
@ -50,40 +51,40 @@ namespace osu.Game.Rulesets.Catch
|
||||
|
||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||
{
|
||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
||||
if (mods.HasFlagFast(LegacyMods.Nightcore))
|
||||
yield return new CatchModNightcore();
|
||||
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
||||
else if (mods.HasFlagFast(LegacyMods.DoubleTime))
|
||||
yield return new CatchModDoubleTime();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Perfect))
|
||||
if (mods.HasFlagFast(LegacyMods.Perfect))
|
||||
yield return new CatchModPerfect();
|
||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
||||
else if (mods.HasFlagFast(LegacyMods.SuddenDeath))
|
||||
yield return new CatchModSuddenDeath();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Cinema))
|
||||
if (mods.HasFlagFast(LegacyMods.Cinema))
|
||||
yield return new CatchModCinema();
|
||||
else if (mods.HasFlag(LegacyMods.Autoplay))
|
||||
else if (mods.HasFlagFast(LegacyMods.Autoplay))
|
||||
yield return new CatchModAutoplay();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Easy))
|
||||
if (mods.HasFlagFast(LegacyMods.Easy))
|
||||
yield return new CatchModEasy();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Flashlight))
|
||||
if (mods.HasFlagFast(LegacyMods.Flashlight))
|
||||
yield return new CatchModFlashlight();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.HalfTime))
|
||||
if (mods.HasFlagFast(LegacyMods.HalfTime))
|
||||
yield return new CatchModHalfTime();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.HardRock))
|
||||
if (mods.HasFlagFast(LegacyMods.HardRock))
|
||||
yield return new CatchModHardRock();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Hidden))
|
||||
if (mods.HasFlagFast(LegacyMods.Hidden))
|
||||
yield return new CatchModHidden();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.NoFail))
|
||||
if (mods.HasFlagFast(LegacyMods.NoFail))
|
||||
yield return new CatchModNoFail();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Relax))
|
||||
if (mods.HasFlagFast(LegacyMods.Relax))
|
||||
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;
|
||||
|
||||
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
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.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
{
|
||||
@ -25,7 +26,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
private float lastDistanceMoved;
|
||||
private double lastStrainTime;
|
||||
|
||||
public Movement(float halfCatcherWidth)
|
||||
public Movement(Mod[] mods, float halfCatcherWidth)
|
||||
: base(mods)
|
||||
{
|
||||
HalfCatcherWidth = halfCatcherWidth;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -97,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
}
|
||||
|
||||
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)
|
||||
=> verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor));
|
||||
|
@ -2,10 +2,10 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.MathUtils;
|
||||
@ -141,7 +142,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
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.85, 0.36, 0.03);
|
||||
@ -149,7 +150,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
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.56, 0.18, 0);
|
||||
@ -157,13 +158,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
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.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.27, 0, 0);
|
||||
@ -221,7 +222,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
var pattern = new Pattern();
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
|
||||
bool canGenerateTwoNotes = !convertType.HasFlagFast(PatternType.LowProbability);
|
||||
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(StartTime).Any(isDoubleSample);
|
||||
|
||||
if (canGenerateTwoNotes)
|
||||
@ -406,7 +407,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int endTime = startTime + SegmentDuration * SpanCount;
|
||||
|
||||
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);
|
||||
|
||||
for (int i = 0; i < columnRepeat; i++)
|
||||
@ -435,7 +436,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
var pattern = new Pattern();
|
||||
|
||||
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);
|
||||
|
||||
// Create the hold note
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osuTK;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
else
|
||||
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)
|
||||
convertType |= PatternType.Mirror;
|
||||
@ -101,7 +102,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
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
|
||||
for (int i = RandomStart; i < TotalColumns; i++)
|
||||
@ -113,11 +114,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
|
||||
// If we convert to 7K + 1, let's not overload the special key
|
||||
&& (TotalColumns != 8 || lastColumn != 0)
|
||||
// Make sure the last column was not the centre column
|
||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||
if (convertType.HasFlagFast(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
|
||||
// If we convert to 7K + 1, let's not overload the special key
|
||||
&& (TotalColumns != 8 || lastColumn != 0)
|
||||
// Make sure the last column was not the centre column
|
||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||
{
|
||||
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
||||
int column = RandomStart + TotalColumns - lastColumn - 1;
|
||||
@ -126,7 +127,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
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
|
||||
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 (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"
|
||||
int targetColumn = lastColumn + 1;
|
||||
@ -151,7 +152,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
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"
|
||||
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);
|
||||
|
||||
if (convertType.HasFlag(PatternType.Mirror))
|
||||
if (convertType.HasFlagFast(PatternType.Mirror))
|
||||
{
|
||||
if (ConversionDifficulty > 6.5)
|
||||
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 (convertType.HasFlag(PatternType.LowProbability))
|
||||
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||
return generateRandomPattern(0.78, 0.42, 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 (convertType.HasFlag(PatternType.LowProbability))
|
||||
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||
return generateRandomPattern(0.35, 0.08, 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 (convertType.HasFlag(PatternType.LowProbability))
|
||||
if (convertType.HasFlagFast(PatternType.LowProbability))
|
||||
return generateRandomPattern(0.18, 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)
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.Stair) && obj.Column == TotalColumns - 1)
|
||||
if (convertType.HasFlagFast(PatternType.Stair) && obj.Column == TotalColumns - 1)
|
||||
StairType = PatternType.ReverseStair;
|
||||
if (convertType.HasFlag(PatternType.ReverseStair) && obj.Column == RandomStart)
|
||||
if (convertType.HasFlagFast(PatternType.ReverseStair) && obj.Column == RandomStart)
|
||||
StairType = PatternType.Stair;
|
||||
}
|
||||
|
||||
@ -229,7 +230,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
var pattern = new Pattern();
|
||||
|
||||
bool allowStacking = !convertType.HasFlag(PatternType.ForceNotStack);
|
||||
bool allowStacking = !convertType.HasFlagFast(PatternType.ForceNotStack);
|
||||
|
||||
if (!allowStacking)
|
||||
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
|
||||
@ -249,7 +250,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
int getNextColumn(int last)
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.Gathered))
|
||||
if (convertType.HasFlagFast(PatternType.Gathered))
|
||||
{
|
||||
last++;
|
||||
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>
|
||||
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);
|
||||
|
||||
var pattern = new Pattern();
|
||||
|
@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
||||
{
|
||||
base.InitialiseDefaults();
|
||||
|
||||
Set(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5);
|
||||
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||
SetDefault(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5);
|
||||
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
@ -24,7 +25,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
private double individualStrain;
|
||||
private double overallStrain;
|
||||
|
||||
public Strain(int totalColumns)
|
||||
public Strain(Mod[] mods, int totalColumns)
|
||||
: base(mods)
|
||||
{
|
||||
holdEndTimes = new double[totalColumns];
|
||||
individualStrains = new double[totalColumns];
|
||||
|
@ -45,6 +45,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
int minColumn = int.MaxValue;
|
||||
int maxColumn = int.MinValue;
|
||||
|
||||
// find min/max in an initial pass before actually performing the movement.
|
||||
foreach (var obj in EditorBeatmap.SelectedHitObjects.OfType<ManiaHitObject>())
|
||||
{
|
||||
if (obj.Column < minColumn)
|
||||
@ -55,8 +56,11 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
|
||||
columnDelta = Math.Clamp(columnDelta, -minColumn, maniaPlayfield.TotalColumns - 1 - maxColumn);
|
||||
|
||||
foreach (var obj in EditorBeatmap.SelectedHitObjects.OfType<ManiaHitObject>())
|
||||
obj.Column += columnDelta;
|
||||
EditorBeatmap.PerformOnSelection(h =>
|
||||
{
|
||||
if (h is ManiaHitObject maniaObj)
|
||||
maniaObj.Column += columnDelta;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
@ -59,76 +60,76 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||
{
|
||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
||||
if (mods.HasFlagFast(LegacyMods.Nightcore))
|
||||
yield return new ManiaModNightcore();
|
||||
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
||||
else if (mods.HasFlagFast(LegacyMods.DoubleTime))
|
||||
yield return new ManiaModDoubleTime();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Perfect))
|
||||
if (mods.HasFlagFast(LegacyMods.Perfect))
|
||||
yield return new ManiaModPerfect();
|
||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
||||
else if (mods.HasFlagFast(LegacyMods.SuddenDeath))
|
||||
yield return new ManiaModSuddenDeath();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Cinema))
|
||||
if (mods.HasFlagFast(LegacyMods.Cinema))
|
||||
yield return new ManiaModCinema();
|
||||
else if (mods.HasFlag(LegacyMods.Autoplay))
|
||||
else if (mods.HasFlagFast(LegacyMods.Autoplay))
|
||||
yield return new ManiaModAutoplay();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Easy))
|
||||
if (mods.HasFlagFast(LegacyMods.Easy))
|
||||
yield return new ManiaModEasy();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.FadeIn))
|
||||
if (mods.HasFlagFast(LegacyMods.FadeIn))
|
||||
yield return new ManiaModFadeIn();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Flashlight))
|
||||
if (mods.HasFlagFast(LegacyMods.Flashlight))
|
||||
yield return new ManiaModFlashlight();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.HalfTime))
|
||||
if (mods.HasFlagFast(LegacyMods.HalfTime))
|
||||
yield return new ManiaModHalfTime();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.HardRock))
|
||||
if (mods.HasFlagFast(LegacyMods.HardRock))
|
||||
yield return new ManiaModHardRock();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Hidden))
|
||||
if (mods.HasFlagFast(LegacyMods.Hidden))
|
||||
yield return new ManiaModHidden();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Key1))
|
||||
if (mods.HasFlagFast(LegacyMods.Key1))
|
||||
yield return new ManiaModKey1();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Key2))
|
||||
if (mods.HasFlagFast(LegacyMods.Key2))
|
||||
yield return new ManiaModKey2();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Key3))
|
||||
if (mods.HasFlagFast(LegacyMods.Key3))
|
||||
yield return new ManiaModKey3();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Key4))
|
||||
if (mods.HasFlagFast(LegacyMods.Key4))
|
||||
yield return new ManiaModKey4();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Key5))
|
||||
if (mods.HasFlagFast(LegacyMods.Key5))
|
||||
yield return new ManiaModKey5();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Key6))
|
||||
if (mods.HasFlagFast(LegacyMods.Key6))
|
||||
yield return new ManiaModKey6();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Key7))
|
||||
if (mods.HasFlagFast(LegacyMods.Key7))
|
||||
yield return new ManiaModKey7();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Key8))
|
||||
if (mods.HasFlagFast(LegacyMods.Key8))
|
||||
yield return new ManiaModKey8();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Key9))
|
||||
if (mods.HasFlagFast(LegacyMods.Key9))
|
||||
yield return new ManiaModKey9();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.KeyCoop))
|
||||
if (mods.HasFlagFast(LegacyMods.KeyCoop))
|
||||
yield return new ManiaModDualStages();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.NoFail))
|
||||
if (mods.HasFlagFast(LegacyMods.NoFail))
|
||||
yield return new ManiaModNoFail();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Random))
|
||||
if (mods.HasFlagFast(LegacyMods.Random))
|
||||
yield return new ManiaModRandom();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Mirror))
|
||||
if (mods.HasFlagFast(LegacyMods.Mirror))
|
||||
yield return new ManiaModMirror();
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,20 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModFadeIn : ManiaModHidden
|
||||
public class ManiaModFadeIn : ManiaModPlayfieldCover
|
||||
{
|
||||
public override string Name => "Fade In";
|
||||
public override string Acronym => "FI";
|
||||
public override IconUsage? Icon => OsuIcon.ModHidden;
|
||||
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;
|
||||
}
|
||||
|
@ -3,43 +3,17 @@
|
||||
|
||||
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 class ManiaModHidden : ModHidden, IApplicableToDrawableRuleset<ManiaHitObject>
|
||||
public class ManiaModHidden : ManiaModPlayfieldCover
|
||||
{
|
||||
public override string Description => @"Keys fade out before you hit them!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
|
||||
|
||||
/// <summary>
|
||||
/// The direction in which the cover should expand.
|
||||
/// </summary>
|
||||
protected virtual CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModFadeIn)).ToArray();
|
||||
|
||||
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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
||||
}
|
||||
}
|
||||
|
43
osu.Game.Rulesets.Mania/Mods/ManiaModPlayfieldCover.cs
Normal file
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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Test]
|
||||
public void TestHitLightingDisabled()
|
||||
{
|
||||
AddStep("hit lighting disabled", () => config.Set(OsuSetting.HitLighting, false));
|
||||
AddStep("hit lighting disabled", () => config.SetValue(OsuSetting.HitLighting, false));
|
||||
|
||||
showResult(HitResult.Great);
|
||||
|
||||
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Test]
|
||||
public void TestHitLightingEnabled()
|
||||
{
|
||||
AddStep("hit lighting enabled", () => config.Set(OsuSetting.HitLighting, true));
|
||||
AddStep("hit lighting enabled", () => config.SetValue(OsuSetting.HitLighting, true));
|
||||
|
||||
showResult(HitResult.Great);
|
||||
|
||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
AddSliderStep("circle size", 0f, 10f, 0f, val =>
|
||||
{
|
||||
config.Set(OsuSetting.AutoCursorSize, true);
|
||||
config.SetValue(OsuSetting.AutoCursorSize, true);
|
||||
gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = val;
|
||||
Scheduler.AddOnce(recreate);
|
||||
});
|
||||
@ -64,21 +64,21 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase(10, 1.5f)]
|
||||
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("turn on autosizing", () => config.Set(OsuSetting.AutoCursorSize, true));
|
||||
AddStep("turn on autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, true));
|
||||
|
||||
AddStep("load content", loadContent);
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -42,10 +42,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
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");
|
||||
|
||||
AddStep("disable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, false));
|
||||
AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false));
|
||||
checkNextHitObject("user");
|
||||
|
||||
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 beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, true));
|
||||
AddStep("enable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, true));
|
||||
AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true));
|
||||
AddStep("enable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, true));
|
||||
checkNextHitObject("beatmap");
|
||||
|
||||
AddStep("enable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, true));
|
||||
AddStep("disable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, false));
|
||||
AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true));
|
||||
AddStep("disable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, false));
|
||||
checkNextHitObject("beatmap");
|
||||
|
||||
AddStep("disable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, false));
|
||||
AddStep("enable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, true));
|
||||
AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false));
|
||||
AddStep("enable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, true));
|
||||
checkNextHitObject("user");
|
||||
|
||||
AddStep("disable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, false));
|
||||
AddStep("disable beatmap colours", () => LocalConfig.Set<bool>(OsuSetting.BeatmapColours, false));
|
||||
AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false));
|
||||
AddStep("disable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, false));
|
||||
checkNextHitObject("user");
|
||||
|
||||
AddStep("disable user provider", () => testUserSkin.Enabled = false);
|
||||
|
@ -2,10 +2,10 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
@ -17,10 +17,10 @@ namespace osu.Game.Rulesets.Osu.Configuration
|
||||
protected override void InitialiseDefaults()
|
||||
{
|
||||
base.InitialiseDefaults();
|
||||
Set(OsuRulesetSetting.SnakingInSliders, true);
|
||||
Set(OsuRulesetSetting.SnakingOutSliders, true);
|
||||
Set(OsuRulesetSetting.ShowCursorTrail, true);
|
||||
Set(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None);
|
||||
SetDefault(OsuRulesetSetting.SnakingInSliders, true);
|
||||
SetDefault(OsuRulesetSetting.SnakingOutSliders, true);
|
||||
SetDefault(OsuRulesetSetting.ShowCursorTrail, true);
|
||||
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 Speed()
|
||||
new Aim(mods),
|
||||
new Speed(mods)
|
||||
};
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||
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 timing_threshold = 107;
|
||||
|
||||
public Aim(Mod[] mods)
|
||||
: base(mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override double SkillMultiplier => 26.25;
|
||||
protected override double StrainDecayBase => 0.15;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||
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 speed_balancing_factor = 40;
|
||||
|
||||
public Speed(Mod[] mods)
|
||||
: base(mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
if (current.BaseObject is Spinner)
|
||||
|
@ -38,6 +38,11 @@ namespace osu.Game.Rulesets.Osu.Judgements
|
||||
/// </example>
|
||||
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>
|
||||
/// 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.
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override ModType Type => ModType.Automation;
|
||||
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
|
||||
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;
|
||||
|
||||
|
@ -11,7 +11,7 @@ using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
internal class OsuModTraceable : ModWithVisibilityAdjustment
|
||||
public class OsuModTraceable : ModWithVisibilityAdjustment
|
||||
{
|
||||
public override string Name => "Traceable";
|
||||
public override string Acronym => "TC";
|
||||
|
@ -158,6 +158,17 @@ 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)
|
||||
{
|
||||
base.UpdateHitStateTransforms(state);
|
||||
@ -262,13 +273,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private void fadeInCounter() => SpmCounter.FadeIn(HitObject.TimeFadeIn);
|
||||
|
||||
private int wholeSpins;
|
||||
|
||||
private void updateBonusScore()
|
||||
|
@ -29,6 +29,7 @@ using osu.Game.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
||||
using osu.Game.Rulesets.Osu.Statistics;
|
||||
@ -58,52 +59,52 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||
{
|
||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
||||
if (mods.HasFlagFast(LegacyMods.Nightcore))
|
||||
yield return new OsuModNightcore();
|
||||
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
||||
else if (mods.HasFlagFast(LegacyMods.DoubleTime))
|
||||
yield return new OsuModDoubleTime();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Perfect))
|
||||
if (mods.HasFlagFast(LegacyMods.Perfect))
|
||||
yield return new OsuModPerfect();
|
||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
||||
else if (mods.HasFlagFast(LegacyMods.SuddenDeath))
|
||||
yield return new OsuModSuddenDeath();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Autopilot))
|
||||
if (mods.HasFlagFast(LegacyMods.Autopilot))
|
||||
yield return new OsuModAutopilot();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Cinema))
|
||||
if (mods.HasFlagFast(LegacyMods.Cinema))
|
||||
yield return new OsuModCinema();
|
||||
else if (mods.HasFlag(LegacyMods.Autoplay))
|
||||
else if (mods.HasFlagFast(LegacyMods.Autoplay))
|
||||
yield return new OsuModAutoplay();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Easy))
|
||||
if (mods.HasFlagFast(LegacyMods.Easy))
|
||||
yield return new OsuModEasy();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Flashlight))
|
||||
if (mods.HasFlagFast(LegacyMods.Flashlight))
|
||||
yield return new OsuModFlashlight();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.HalfTime))
|
||||
if (mods.HasFlagFast(LegacyMods.HalfTime))
|
||||
yield return new OsuModHalfTime();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.HardRock))
|
||||
if (mods.HasFlagFast(LegacyMods.HardRock))
|
||||
yield return new OsuModHardRock();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Hidden))
|
||||
if (mods.HasFlagFast(LegacyMods.Hidden))
|
||||
yield return new OsuModHidden();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.NoFail))
|
||||
if (mods.HasFlagFast(LegacyMods.NoFail))
|
||||
yield return new OsuModNoFail();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Relax))
|
||||
if (mods.HasFlagFast(LegacyMods.Relax))
|
||||
yield return new OsuModRelax();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.SpunOut))
|
||||
if (mods.HasFlagFast(LegacyMods.SpunOut))
|
||||
yield return new OsuModSpunOut();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Target))
|
||||
if (mods.HasFlagFast(LegacyMods.Target))
|
||||
yield return new OsuModTarget();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.TouchDevice))
|
||||
if (mods.HasFlagFast(LegacyMods.TouchDevice))
|
||||
yield return new OsuModTouchDevice();
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => number.Text;
|
||||
get => number.Text.ToString();
|
||||
set => number.Text = value;
|
||||
}
|
||||
|
||||
|
@ -37,9 +37,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
AddInternal(scaleContainer = new Container
|
||||
{
|
||||
Scale = new Vector2(SPRITE_SCALE),
|
||||
Anchor = Anchor.Centre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Y = SPINNER_Y_CENTRE,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
glow = new Sprite
|
||||
|
@ -33,47 +33,38 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
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.
|
||||
// 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
|
||||
{
|
||||
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,
|
||||
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.
|
||||
Texture = source.GetTexture("spinner-metre"),
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
// adjustment for stable (metre has additional offset)
|
||||
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)
|
||||
}
|
||||
Scale = new Vector2(SPRITE_SCALE)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -16,6 +16,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
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 DrawableSpinner DrawableSpinner { get; private set; }
|
||||
@ -26,7 +35,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
[BackgroundDependencyLoader]
|
||||
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;
|
||||
|
||||
@ -34,22 +49,22 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
spin = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
Depth = float.MinValue,
|
||||
Texture = source.GetTexture("spinner-spin"),
|
||||
Scale = new Vector2(SPRITE_SCALE),
|
||||
Y = 120 - 45 // offset temporarily to avoid overlapping default spin counter
|
||||
Y = SPINNER_TOP_OFFSET + 335,
|
||||
},
|
||||
clear = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
Depth = float.MinValue,
|
||||
Alpha = 0,
|
||||
Texture = source.GetTexture("spinner-clear"),
|
||||
Scale = new Vector2(SPRITE_SCALE),
|
||||
Y = -60
|
||||
Y = SPINNER_TOP_OFFSET + 115,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -2,8 +2,12 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
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.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
@ -13,6 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[TestFixture]
|
||||
public class TestSceneHitExplosion : TaikoSkinnableTestScene
|
||||
{
|
||||
protected override double TimePerAction => 100;
|
||||
|
||||
[Test]
|
||||
public void TestNormalHit()
|
||||
{
|
||||
@ -21,11 +27,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStrongHit([Values(false, true)] bool hitBoth)
|
||||
[TestCase(HitResult.Great)]
|
||||
[TestCase(HitResult.Ok)]
|
||||
public void TestStrongHit(HitResult type)
|
||||
{
|
||||
AddStep("Great", () => SetContents(() => getContentFor(createStrongHit(HitResult.Great, hitBoth))));
|
||||
AddStep("Good", () => SetContents(() => getContentFor(createStrongHit(HitResult.Ok, hitBoth))));
|
||||
AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type))));
|
||||
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)
|
||||
@ -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.
|
||||
// setting zero alpha is supposed to prevent the test from looking broken.
|
||||
hit.With(h => h.Alpha = 0),
|
||||
new HitExplosion(hit, hit.Type)
|
||||
new HitExplosion(hit.Type)
|
||||
{
|
||||
Anchor = 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 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.ControlPoints;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
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;
|
||||
|
||||
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) };
|
||||
|
||||
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)
|
||||
@ -122,6 +121,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
|
||||
Hit hit = new Hit
|
||||
{
|
||||
StartTime = DrawableRuleset.Playfield.Time.Current,
|
||||
IsStrong = true,
|
||||
Samples = createSamples(strong: true)
|
||||
};
|
||||
@ -129,8 +129,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
|
||||
DrawableRuleset.Playfield.Add(h);
|
||||
|
||||
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), 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, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult });
|
||||
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(hit.NestedHitObjects.Single(), new TaikoStrongJudgement()) { Type = HitResult.Great });
|
||||
}
|
||||
|
||||
private void addMissJudgement()
|
||||
|
@ -2,10 +2,10 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
|
||||
@ -39,6 +40,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// </summary>
|
||||
private int currentMonoLength;
|
||||
|
||||
public Colour(Mod[] mods)
|
||||
: base(mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
// 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.Skills;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
|
||||
@ -47,6 +48,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// </summary>
|
||||
private int notesSinceRhythmChange;
|
||||
|
||||
public Rhythm(Mod[] mods)
|
||||
: base(mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
// drum rolls and swells are exempt.
|
||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
|
||||
@ -48,8 +49,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Stamina"/> skill.
|
||||
/// </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>
|
||||
public Stamina(bool rightHand)
|
||||
public Stamina(Mod[] mods, bool rightHand)
|
||||
: base(mods)
|
||||
{
|
||||
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 Rhythm(),
|
||||
new Stamina(true),
|
||||
new Stamina(false),
|
||||
new Colour(mods),
|
||||
new Rhythm(mods),
|
||||
new Stamina(mods, true),
|
||||
new Stamina(mods, false),
|
||||
};
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||
|
@ -52,32 +52,24 @@ namespace osu.Game.Rulesets.Taiko.Edit
|
||||
|
||||
public void SetStrongState(bool state)
|
||||
{
|
||||
var hits = EditorBeatmap.SelectedHitObjects.OfType<Hit>();
|
||||
|
||||
EditorBeatmap.BeginChange();
|
||||
|
||||
foreach (var h in hits)
|
||||
EditorBeatmap.PerformOnSelection(h =>
|
||||
{
|
||||
if (h.IsStrong != state)
|
||||
{
|
||||
h.IsStrong = state;
|
||||
EditorBeatmap.Update(h);
|
||||
}
|
||||
}
|
||||
if (!(h is Hit taikoHit)) return;
|
||||
|
||||
EditorBeatmap.EndChange();
|
||||
if (taikoHit.IsStrong != state)
|
||||
{
|
||||
taikoHit.IsStrong = state;
|
||||
EditorBeatmap.Update(taikoHit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void SetRimState(bool state)
|
||||
{
|
||||
var hits = EditorBeatmap.SelectedHitObjects.OfType<Hit>();
|
||||
|
||||
EditorBeatmap.BeginChange();
|
||||
|
||||
foreach (var h in hits)
|
||||
h.Type = state ? HitType.Rim : HitType.Centre;
|
||||
|
||||
EditorBeatmap.EndChange();
|
||||
EditorBeatmap.PerformOnSelection(h =>
|
||||
{
|
||||
if (h is Hit taikoHit) taikoHit.Type = state ? HitType.Rim : HitType.Centre;
|
||||
});
|
||||
}
|
||||
|
||||
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.
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
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
|
||||
{
|
||||
public class LegacyHitExplosion : CompositeDrawable
|
||||
public class LegacyHitExplosion : CompositeDrawable, IAnimatableHitExplosion
|
||||
{
|
||||
private readonly Drawable sprite;
|
||||
private readonly Drawable strongSprite;
|
||||
|
||||
private DrawableStrongNestedHit nestedStrongHit;
|
||||
private bool switchedToStrongSprite;
|
||||
[CanBeNull]
|
||||
private readonly Drawable strongSprite;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new legacy hit explosion.
|
||||
@ -27,14 +27,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
/// </remarks>
|
||||
/// <param name="sprite">The normal 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.strongSprite = strongSprite;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(DrawableHitObject judgedObject)
|
||||
private void load()
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
@ -56,45 +56,30 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
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;
|
||||
|
||||
(sprite as IFramedAnimation)?.GotoFrame(0);
|
||||
(strongSprite as IFramedAnimation)?.GotoFrame(0);
|
||||
|
||||
this.FadeInFromZero(animation_time).Then().FadeOut(animation_time * 1.5);
|
||||
|
||||
this.ScaleTo(0.6f)
|
||||
.Then().ScaleTo(1.1f, animation_time * 0.8)
|
||||
.Then().ScaleTo(0.9f, animation_time * 0.4)
|
||||
.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);
|
||||
switchedToStrongSprite = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool shouldSwitchToStrongSprite()
|
||||
{
|
||||
if (nestedStrongHit == null || strongSprite == null)
|
||||
return false;
|
||||
|
||||
return nestedStrongHit.IsHit;
|
||||
sprite.FadeOut(50, Easing.OutQuint);
|
||||
strongSprite.FadeIn(50, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
{
|
||||
// if a taiko skin is providing explosion sprites, hide the judgements completely
|
||||
if (hasExplosion.Value)
|
||||
return Drawable.Empty();
|
||||
return Drawable.Empty().With(d => d.Expire());
|
||||
}
|
||||
|
||||
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.
|
||||
// the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield.
|
||||
if (hasExplosion.Value)
|
||||
return Drawable.Empty().With(d => d.LifetimeEnd = double.MinValue);
|
||||
return Drawable.Empty().With(d => d.Expire());
|
||||
|
||||
return null;
|
||||
|
||||
|
@ -22,6 +22,7 @@ using osu.Game.Rulesets.Taiko.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Taiko.Edit;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@ -57,43 +58,43 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||
{
|
||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
||||
if (mods.HasFlagFast(LegacyMods.Nightcore))
|
||||
yield return new TaikoModNightcore();
|
||||
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
||||
else if (mods.HasFlagFast(LegacyMods.DoubleTime))
|
||||
yield return new TaikoModDoubleTime();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Perfect))
|
||||
if (mods.HasFlagFast(LegacyMods.Perfect))
|
||||
yield return new TaikoModPerfect();
|
||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
||||
else if (mods.HasFlagFast(LegacyMods.SuddenDeath))
|
||||
yield return new TaikoModSuddenDeath();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Cinema))
|
||||
if (mods.HasFlagFast(LegacyMods.Cinema))
|
||||
yield return new TaikoModCinema();
|
||||
else if (mods.HasFlag(LegacyMods.Autoplay))
|
||||
else if (mods.HasFlagFast(LegacyMods.Autoplay))
|
||||
yield return new TaikoModAutoplay();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Easy))
|
||||
if (mods.HasFlagFast(LegacyMods.Easy))
|
||||
yield return new TaikoModEasy();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Flashlight))
|
||||
if (mods.HasFlagFast(LegacyMods.Flashlight))
|
||||
yield return new TaikoModFlashlight();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.HalfTime))
|
||||
if (mods.HasFlagFast(LegacyMods.HalfTime))
|
||||
yield return new TaikoModHalfTime();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.HardRock))
|
||||
if (mods.HasFlagFast(LegacyMods.HardRock))
|
||||
yield return new TaikoModHardRock();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Hidden))
|
||||
if (mods.HasFlagFast(LegacyMods.Hidden))
|
||||
yield return new TaikoModHidden();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.NoFail))
|
||||
if (mods.HasFlagFast(LegacyMods.NoFail))
|
||||
yield return new TaikoModNoFail();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Relax))
|
||||
if (mods.HasFlagFast(LegacyMods.Relax))
|
||||
yield return new TaikoModRelax();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.Random))
|
||||
if (mods.HasFlagFast(LegacyMods.Random))
|
||||
yield return new TaikoModRandom();
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -13,19 +14,23 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
internal class DefaultHitExplosion : CircularContainer
|
||||
internal class DefaultHitExplosion : CircularContainer, IAnimatableHitExplosion
|
||||
{
|
||||
private readonly DrawableHitObject judgedObject;
|
||||
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;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@ -40,26 +45,36 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
if (!result.IsHit())
|
||||
return;
|
||||
|
||||
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new Box
|
||||
body = new Box
|
||||
{
|
||||
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.FadeOut(500);
|
||||
}
|
||||
|
||||
Expire(true);
|
||||
public void AnimateSecondHit()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
@ -12,16 +11,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
/// </summary>
|
||||
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()
|
||||
{
|
||||
this.MoveToY(-100, 500);
|
||||
|
@ -2,10 +2,12 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osuTK;
|
||||
using osu.Framework.Allocation;
|
||||
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.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@ -16,31 +18,37 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
/// <summary>
|
||||
/// A circle explodes from the hit target to indicate a hitobject has been hit.
|
||||
/// </summary>
|
||||
internal class HitExplosion : CircularContainer
|
||||
internal class HitExplosion : PoolableDrawable
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => true;
|
||||
|
||||
[Cached(typeof(DrawableHitObject))]
|
||||
public readonly DrawableHitObject JudgedObject;
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
private readonly HitResult result;
|
||||
|
||||
private double? secondHitTime;
|
||||
|
||||
[CanBeNull]
|
||||
public DrawableHitObject JudgedObject;
|
||||
|
||||
private SkinnableDrawable skinnable;
|
||||
|
||||
public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
|
||||
|
||||
public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
|
||||
|
||||
public HitExplosion(DrawableHitObject judgedObject, HitResult result)
|
||||
/// <summary>
|
||||
/// This constructor only exists to meet the <c>new()</c> type constraint of <see cref="DrawablePool{T}"/>.
|
||||
/// </summary>
|
||||
public HitExplosion()
|
||||
: this(HitResult.Great)
|
||||
{
|
||||
}
|
||||
|
||||
public HitExplosion(HitResult result)
|
||||
{
|
||||
JudgedObject = judgedObject;
|
||||
this.result = result;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE);
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
RelativePositionAxes = Axes.Both;
|
||||
}
|
||||
@ -48,7 +56,47 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
[BackgroundDependencyLoader]
|
||||
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)
|
||||
@ -68,12 +116,10 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms this hit explosion to visualise a secondary hit.
|
||||
/// </summary>
|
||||
public void VisualiseSecondHit()
|
||||
public void VisualiseSecondHit(JudgementResult judgementResult)
|
||||
{
|
||||
this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50);
|
||||
secondHitTime = judgementResult.TimeAbsolute;
|
||||
runAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs
Normal file
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
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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
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.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
@ -38,6 +41,9 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
internal Drawable HitTarget;
|
||||
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 Container rightArea;
|
||||
private Container leftArea;
|
||||
@ -159,6 +165,17 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
RegisterPool<Swell, DrawableSwell>(5);
|
||||
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()
|
||||
@ -270,7 +287,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
case TaikoStrongJudgement _:
|
||||
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;
|
||||
|
||||
case TaikoDrumRollTickJudgement _:
|
||||
@ -283,13 +300,15 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
break;
|
||||
|
||||
default:
|
||||
judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject)
|
||||
judgementContainer.Add(judgementPools[result.Type].Get(j =>
|
||||
{
|
||||
Anchor = result.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
|
||||
Origin = result.IsHit ? Anchor.BottomCentre : Anchor.Centre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = result.IsHit ? judgedObject.Position.X : 0,
|
||||
});
|
||||
j.Apply(result, judgedObject);
|
||||
|
||||
j.Anchor = result.IsHit ? Anchor.TopLeft : Anchor.CentreLeft;
|
||||
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;
|
||||
addExplosion(judgedObject, result.Type, type);
|
||||
@ -302,7 +321,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
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)
|
||||
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
|
||||
}
|
||||
|
@ -20,6 +20,9 @@
|
||||
<ItemGroup>
|
||||
<None Include="Properties\AndroidManifest.xml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);CA2007</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\osu.Game.Tests\**\Beatmaps\**\*.cs">
|
||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
@ -71,7 +74,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="DeepEqual" Version="2.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -21,6 +21,9 @@
|
||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);CA2007</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
|
||||
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project>
|
||||
@ -45,7 +48,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="DeepEqual" Version="2.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -852,6 +852,21 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<BeatmapSetInfo> LoadQuickOszIntoOsu(OsuGameBase osu)
|
||||
{
|
||||
var temp = TestResources.GetQuickTestBeatmapForImport();
|
||||
|
||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||
|
||||
var importedSet = await manager.Import(new ImportTask(temp));
|
||||
|
||||
ensureLoaded(osu);
|
||||
|
||||
waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000);
|
||||
|
||||
return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID);
|
||||
}
|
||||
|
||||
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
|
||||
{
|
||||
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
|
||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets;
|
||||
@ -90,6 +91,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
public void TestSamplePlaybackWithRateMods(Type expectedMod, double expectedRate)
|
||||
{
|
||||
GameplayClockContainer gameplayContainer = null;
|
||||
StoryboardSampleInfo sampleInfo = null;
|
||||
TestDrawableStoryboardSample sample = null;
|
||||
|
||||
Mod testedMod = Activator.CreateInstance(expectedMod) as Mod;
|
||||
@ -101,7 +103,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
break;
|
||||
|
||||
case ModTimeRamp m:
|
||||
m.InitialRate.Value = m.FinalRate.Value = expectedRate;
|
||||
m.FinalRate.Value = m.InitialRate.Value = expectedRate;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -117,7 +119,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
Child = beatmapSkinSourceContainer
|
||||
});
|
||||
|
||||
beatmapSkinSourceContainer.Add(sample = new TestDrawableStoryboardSample(new StoryboardSampleInfo("test-sample", 1, 1))
|
||||
beatmapSkinSourceContainer.Add(sample = new TestDrawableStoryboardSample(sampleInfo = new StoryboardSampleInfo("test-sample", 1, 1))
|
||||
{
|
||||
Clock = gameplayContainer.GameplayClock
|
||||
});
|
||||
@ -125,7 +127,10 @@ namespace osu.Game.Tests.Gameplay
|
||||
|
||||
AddStep("start", () => gameplayContainer.Start());
|
||||
|
||||
AddAssert("sample playback rate matches mod rates", () => sample.ChildrenOfType<DrawableSample>().First().AggregateFrequency.Value == expectedRate);
|
||||
AddAssert("sample playback rate matches mod rates", () =>
|
||||
testedMod != null && Precision.AlmostEquals(
|
||||
sample.ChildrenOfType<DrawableSample>().First().AggregateFrequency.Value,
|
||||
((IApplicableToRate)testedMod).ApplyToRate(sampleInfo.StartTime)));
|
||||
}
|
||||
|
||||
private class TestSkin : LegacySkin
|
||||
|
@ -88,7 +88,7 @@ namespace osu.Game.Tests.Input
|
||||
=> AddStep($"make window {mode}", () => frameworkConfigManager.GetBindable<WindowMode>(FrameworkSetting.WindowMode).Value = mode);
|
||||
|
||||
private void setGameSideModeTo(OsuConfineMouseMode mode)
|
||||
=> AddStep($"set {mode} game-side", () => Game.LocalConfig.Set(OsuSetting.ConfineMouseMode, mode));
|
||||
=> AddStep($"set {mode} game-side", () => Game.LocalConfig.SetValue(OsuSetting.ConfineMouseMode, mode));
|
||||
|
||||
private void setLocalUserPlayingTo(bool playing)
|
||||
=> AddStep($"local user {(playing ? "playing" : "not playing")}", () => Game.LocalUserPlaying.Value = playing);
|
||||
|
44
osu.Game.Tests/Mods/SettingsSourceAttributeTest.cs
Normal file
44
osu.Game.Tests/Mods/SettingsSourceAttributeTest.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Configuration;
|
||||
|
||||
namespace osu.Game.Tests.Mods
|
||||
{
|
||||
[TestFixture]
|
||||
public class SettingsSourceAttributeTest
|
||||
{
|
||||
[Test]
|
||||
public void TestOrdering()
|
||||
{
|
||||
var objectWithSettings = new ClassWithSettings();
|
||||
|
||||
var orderedSettings = objectWithSettings.GetOrderedSettingsSourceProperties().ToArray();
|
||||
|
||||
Assert.That(orderedSettings, Has.Length.EqualTo(4));
|
||||
|
||||
Assert.That(orderedSettings[0].Item2.Name, Is.EqualTo(nameof(ClassWithSettings.FirstSetting)));
|
||||
Assert.That(orderedSettings[1].Item2.Name, Is.EqualTo(nameof(ClassWithSettings.SecondSetting)));
|
||||
Assert.That(orderedSettings[2].Item2.Name, Is.EqualTo(nameof(ClassWithSettings.ThirdSetting)));
|
||||
Assert.That(orderedSettings[3].Item2.Name, Is.EqualTo(nameof(ClassWithSettings.UnorderedSetting)));
|
||||
}
|
||||
|
||||
private class ClassWithSettings
|
||||
{
|
||||
[SettingSource("Unordered setting", "Should be last")]
|
||||
public BindableFloat UnorderedSetting { get; set; } = new BindableFloat();
|
||||
|
||||
[SettingSource("Second setting", "Another description", 2)]
|
||||
public BindableBool SecondSetting { get; set; } = new BindableBool();
|
||||
|
||||
[SettingSource("First setting", "A description", 1)]
|
||||
public BindableDouble FirstSetting { get; set; } = new BindableDouble();
|
||||
|
||||
[SettingSource("Third setting", "Yet another description", 3)]
|
||||
public BindableInt ThirdSetting { get; set; } = new BindableInt();
|
||||
}
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
using (var host = new CustomTestHeadlessGameHost())
|
||||
{
|
||||
using (var storageConfig = new StorageConfigManager(host.InitialStorage))
|
||||
storageConfig.Set(StorageConfig.FullPath, customPath);
|
||||
storageConfig.SetValue(StorageConfig.FullPath, customPath);
|
||||
|
||||
try
|
||||
{
|
||||
@ -73,7 +73,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
using (var host = new CustomTestHeadlessGameHost())
|
||||
{
|
||||
using (var storageConfig = new StorageConfigManager(host.InitialStorage))
|
||||
storageConfig.Set(StorageConfig.FullPath, customPath);
|
||||
storageConfig.SetValue(StorageConfig.FullPath, customPath);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -212,7 +212,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap)
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -4,8 +4,10 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Filter;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual.Filtering
|
||||
{
|
||||
@ -214,5 +216,31 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
||||
|
||||
Assert.AreEqual(filtered, carouselItem.Filtered.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCustomRulesetCriteria([Values(null, true, false)] bool? matchCustomCriteria)
|
||||
{
|
||||
var beatmap = getExampleBeatmap();
|
||||
|
||||
var customCriteria = matchCustomCriteria is bool match ? new CustomCriteria(match) : null;
|
||||
var criteria = new FilterCriteria { RulesetCriteria = customCriteria };
|
||||
var carouselItem = new CarouselBeatmap(beatmap);
|
||||
carouselItem.Filter(criteria);
|
||||
|
||||
Assert.AreEqual(matchCustomCriteria == false, carouselItem.Filtered.Value);
|
||||
}
|
||||
|
||||
private class CustomCriteria : IRulesetFilterCriteria
|
||||
{
|
||||
private readonly bool match;
|
||||
|
||||
public CustomCriteria(bool shouldMatch)
|
||||
{
|
||||
match = shouldMatch;
|
||||
}
|
||||
|
||||
public bool Matches(BeatmapInfo beatmap) => match;
|
||||
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Filter;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual.Filtering
|
||||
{
|
||||
@ -194,5 +196,63 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
||||
Assert.AreEqual(1, filterCriteria.SearchTerms.Length);
|
||||
Assert.AreEqual("double\"quote", filterCriteria.Artist.SearchTerm);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOperatorParsing()
|
||||
{
|
||||
const string query = "artist=><something";
|
||||
var filterCriteria = new FilterCriteria();
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual("><something", filterCriteria.Artist.SearchTerm);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnrecognisedKeywordIsIgnored()
|
||||
{
|
||||
const string query = "unrecognised=keyword";
|
||||
var filterCriteria = new FilterCriteria();
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual("unrecognised=keyword", filterCriteria.SearchText);
|
||||
}
|
||||
|
||||
[TestCase("cs=nope")]
|
||||
[TestCase("bpm>=bad")]
|
||||
[TestCase("divisor<nah")]
|
||||
[TestCase("status=noidea")]
|
||||
public void TestInvalidKeywordValueIsIgnored(string query)
|
||||
{
|
||||
var filterCriteria = new FilterCriteria();
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual(query, filterCriteria.SearchText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCustomKeywordIsParsed()
|
||||
{
|
||||
var customCriteria = new CustomFilterCriteria();
|
||||
const string query = "custom=readme unrecognised=keyword";
|
||||
var filterCriteria = new FilterCriteria { RulesetCriteria = customCriteria };
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual("readme", customCriteria.CustomValue);
|
||||
Assert.AreEqual("unrecognised=keyword", filterCriteria.SearchText.Trim());
|
||||
}
|
||||
|
||||
private class CustomFilterCriteria : IRulesetFilterCriteria
|
||||
{
|
||||
public string CustomValue { get; set; }
|
||||
|
||||
public bool Matches(BeatmapInfo beatmap) => true;
|
||||
|
||||
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value)
|
||||
{
|
||||
if (key == "custom" && op == Operator.Equal)
|
||||
{
|
||||
CustomValue = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
beatmaps.AllowImport = new TaskCompletionSource<bool>();
|
||||
|
||||
testBeatmapFile = TestResources.GetTestBeatmapForImport();
|
||||
testBeatmapFile = TestResources.GetQuickTestBeatmapForImport();
|
||||
|
||||
testBeatmapInfo = getTestBeatmapInfo(testBeatmapFile);
|
||||
testBeatmapSet = testBeatmapInfo.BeatmapSet;
|
||||
|
35
osu.Game.Tests/OnlinePlay/StatefulMultiplayerClientTest.cs
Normal file
35
osu.Game.Tests/OnlinePlay/StatefulMultiplayerClientTest.cs
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.OnlinePlay
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class StatefulMultiplayerClientTest : MultiplayerTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestUserAddedOnJoin()
|
||||
{
|
||||
var user = new User { Id = 33 };
|
||||
|
||||
AddRepeatStep("add user multiple times", () => Client.AddUser(user), 3);
|
||||
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserRemovedOnLeave()
|
||||
{
|
||||
var user = new User { Id = 44 };
|
||||
|
||||
AddStep("add user", () => Client.AddUser(user));
|
||||
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
|
||||
|
||||
AddRepeatStep("remove user multiple times", () => Client.RemoveUser(user), 3);
|
||||
AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1);
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
@ -15,6 +15,28 @@ namespace osu.Game.Tests.Resources
|
||||
|
||||
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => OpenResource($"Archives/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a path to a copy of a shortened (~10 second) beatmap archive with a virtual track.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is intended for use in tests which need to run to completion as soon as possible and don't need to test a full length beatmap.</remarks>
|
||||
/// <returns>A path to a copy of a beatmap archive (osz). Should be deleted after use.</returns>
|
||||
public static string GetQuickTestBeatmapForImport()
|
||||
{
|
||||
var tempPath = Path.GetTempFileName() + ".osz";
|
||||
using (var stream = OpenResource("Archives/241526 Soleily - Renatus_virtual_quick.osz"))
|
||||
using (var newFile = File.Create(tempPath))
|
||||
stream.CopyTo(newFile);
|
||||
|
||||
Assert.IsTrue(File.Exists(tempPath));
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a path to a copy of a full-fledged beatmap archive.
|
||||
/// </summary>
|
||||
/// <param name="virtualTrack">Whether the audio track should be virtual.</param>
|
||||
/// <returns>A path to a copy of a beatmap archive (osz). Should be deleted after use.</returns>
|
||||
public static string GetTestBeatmapForImport(bool virtualTrack = false)
|
||||
{
|
||||
var tempPath = Path.GetTempFileName() + ".osz";
|
||||
|
2
osu.Game.Tests/Resources/skin-with-space.ini
Normal file
2
osu.Game.Tests/Resources/skin-with-space.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[General]
|
||||
Version: 2
|
@ -91,6 +91,15 @@ namespace osu.Game.Tests.Skins
|
||||
Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStripWhitespace()
|
||||
{
|
||||
var decoder = new LegacySkinDecoder();
|
||||
using (var resStream = TestResources.OpenResource("skin-with-space.ini"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeLatestVersion()
|
||||
{
|
||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
// reset API response in statics to avoid test crosstalk.
|
||||
statics.Set<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null);
|
||||
statics.SetValue<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null);
|
||||
textureStore.PerformedLookups.Clear();
|
||||
dummyAPI.SetState(APIState.Online);
|
||||
|
||||
@ -146,7 +146,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
});
|
||||
|
||||
private void setSeasonalBackgroundMode(SeasonalBackgroundMode mode)
|
||||
=> AddStep($"set seasonal mode to {mode}", () => config.Set(OsuSetting.SeasonalBackgroundMode, mode));
|
||||
=> AddStep($"set seasonal mode to {mode}", () => config.SetValue(OsuSetting.SeasonalBackgroundMode, mode));
|
||||
|
||||
private void createLoader()
|
||||
=> AddStep("create loader", () =>
|
||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
|
||||
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
||||
|
||||
manager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
||||
manager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||
|
||||
Beatmap.SetDefault();
|
||||
}
|
||||
|
@ -38,13 +38,13 @@ namespace osu.Game.Tests.Visual.Collections
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default));
|
||||
|
||||
beatmapManager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
||||
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||
|
||||
base.Content.AddRange(new Drawable[]
|
||||
{
|
||||
manager = new CollectionManager(LocalStorage),
|
||||
Content,
|
||||
dialogOverlay = new DialogOverlay()
|
||||
dialogOverlay = new DialogOverlay(),
|
||||
});
|
||||
|
||||
Dependencies.Cache(manager);
|
||||
@ -134,6 +134,27 @@ namespace osu.Game.Tests.Visual.Collections
|
||||
assertCollectionName(0, "2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCollectionNameCollisions()
|
||||
{
|
||||
AddStep("add dropdown", () =>
|
||||
{
|
||||
Add(new CollectionFilterDropdown
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.4f,
|
||||
}
|
||||
);
|
||||
});
|
||||
AddStep("add two collections with same name", () => manager.Collections.AddRange(new[]
|
||||
{
|
||||
new BeatmapCollection { Name = { Value = "1" } },
|
||||
new BeatmapCollection { Name = { Value = "1" }, Beatmaps = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0] } },
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRemoveCollectionViaButton()
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
|
||||
AddStep("show health", () => showHealth.Value = true);
|
||||
AddStep("enable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddStep("enable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddUntilStep("layer is visible", () => layer.IsPresent);
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestLayerDisabledViaConfig()
|
||||
{
|
||||
AddStep("disable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
|
||||
AddUntilStep("layer is not visible", () => !layer.IsPresent);
|
||||
}
|
||||
@ -81,19 +81,19 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
|
||||
|
||||
AddStep("don't show health", () => showHealth.Value = false);
|
||||
AddStep("disable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddStep("disable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddUntilStep("layer fade is invisible", () => !layer.IsPresent);
|
||||
|
||||
AddStep("don't show health", () => showHealth.Value = false);
|
||||
AddStep("enable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddUntilStep("layer fade is invisible", () => !layer.IsPresent);
|
||||
|
||||
AddStep("show health", () => showHealth.Value = true);
|
||||
AddStep("disable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddStep("disable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddUntilStep("layer fade is invisible", () => !layer.IsPresent);
|
||||
|
||||
AddStep("show health", () => showHealth.Value = true);
|
||||
AddStep("enable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddUntilStep("layer fade is visible", () => layer.IsPresent);
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("get original config value", () => originalConfigValue = config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
|
||||
AddStep("set hud to never show", () => config.Set(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
|
||||
AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
|
||||
|
||||
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
|
||||
|
||||
@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("stop trigering", () => InputManager.ReleaseKey(Key.ControlLeft));
|
||||
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
|
||||
|
||||
AddStep("set original config value", () => config.Set(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||
AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("set keycounter visible false", () =>
|
||||
{
|
||||
config.Set<bool>(OsuSetting.KeyOverlay, false);
|
||||
config.SetValue(OsuSetting.KeyOverlay, false);
|
||||
hudOverlay.KeyCounter.AlwaysVisible.Value = false;
|
||||
});
|
||||
|
||||
@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("hidetarget is visible", () => hideTarget.IsPresent);
|
||||
AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent);
|
||||
|
||||
AddStep("return value", () => config.Set<bool>(OsuSetting.KeyOverlay, keyCounterVisibleValue));
|
||||
AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue));
|
||||
}
|
||||
|
||||
private void createNew(Action<HUDOverlay> action = null)
|
||||
|
@ -46,11 +46,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[TestCase(0, 0)]
|
||||
[TestCase(-1000, -1000)]
|
||||
[TestCase(-10000, -10000)]
|
||||
public void TestStoryboardProducesCorrectStartTime(double firstStoryboardEvent, double expectedStartTime)
|
||||
public void TestStoryboardProducesCorrectStartTimeSimpleAlpha(double firstStoryboardEvent, double expectedStartTime)
|
||||
{
|
||||
var storyboard = new Storyboard();
|
||||
|
||||
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
|
||||
|
||||
sprite.TimelineGroup.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1);
|
||||
|
||||
storyboard.GetLayer("Background").Add(sprite);
|
||||
@ -64,6 +65,43 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1000, 0, false)]
|
||||
[TestCase(0, 0, false)]
|
||||
[TestCase(-1000, -1000, false)]
|
||||
[TestCase(-10000, -10000, false)]
|
||||
[TestCase(1000, 0, true)]
|
||||
[TestCase(0, 0, true)]
|
||||
[TestCase(-1000, -1000, true)]
|
||||
[TestCase(-10000, -10000, true)]
|
||||
public void TestStoryboardProducesCorrectStartTimeFadeInAfterOtherEvents(double firstStoryboardEvent, double expectedStartTime, bool addEventToLoop)
|
||||
{
|
||||
var storyboard = new Storyboard();
|
||||
|
||||
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
|
||||
|
||||
// these should be ignored as we have an alpha visibility blocker proceeding this command.
|
||||
sprite.TimelineGroup.Scale.Add(Easing.None, -20000, -18000, 0, 1);
|
||||
var loopGroup = sprite.AddLoop(-20000, 50);
|
||||
loopGroup.Scale.Add(Easing.None, -20000, -18000, 0, 1);
|
||||
|
||||
var target = addEventToLoop ? loopGroup : sprite.TimelineGroup;
|
||||
target.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1);
|
||||
|
||||
// these should be ignored due to being in the future.
|
||||
sprite.TimelineGroup.Alpha.Add(Easing.None, 18000, 20000, 0, 1);
|
||||
loopGroup.Alpha.Add(Easing.None, 18000, 20000, 0, 1);
|
||||
|
||||
storyboard.GetLayer("Background").Add(sprite);
|
||||
|
||||
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard);
|
||||
|
||||
AddAssert($"first frame is {expectedStartTime}", () =>
|
||||
{
|
||||
Debug.Assert(player.FirstFrameClockTime != null);
|
||||
return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms);
|
||||
});
|
||||
}
|
||||
|
||||
private void loadPlayerWithBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
{
|
||||
AddStep("create player", () =>
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -8,21 +9,17 @@ using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
[HeadlessTest] // we alter unsafe properties on the game host to test inactive window state.
|
||||
public class TestScenePauseWhenInactive : OsuPlayerTestScene
|
||||
{
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
var beatmap = (Beatmap)base.CreateBeatmap(ruleset);
|
||||
|
||||
beatmap.HitObjects.RemoveAll(h => h.StartTime < 30000);
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
@ -33,10 +30,57 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("resume player", () => Player.GameplayClockContainer.Start());
|
||||
AddAssert("ensure not paused", () => !Player.GameplayClockContainer.IsPaused.Value);
|
||||
|
||||
AddStep("progress time to gameplay", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.GameplayStartTime));
|
||||
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that if a pause from focus lose is performed while in pause cooldown,
|
||||
/// the player will still pause after the cooldown is finished.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPauseWhileInCooldown()
|
||||
{
|
||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||
|
||||
AddStep("resume player", () => Player.GameplayClockContainer.Start());
|
||||
AddStep("skip to gameplay", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.GameplayStartTime));
|
||||
|
||||
AddStep("set inactive", () => ((Bindable<bool>)host.IsActive).Value = false);
|
||||
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||
|
||||
AddStep("set active", () => ((Bindable<bool>)host.IsActive).Value = true);
|
||||
|
||||
AddStep("resume player", () => Player.Resume());
|
||||
AddAssert("unpaused", () => !Player.GameplayClockContainer.IsPaused.Value);
|
||||
|
||||
bool pauseCooldownActive = false;
|
||||
|
||||
AddStep("set inactive again", () =>
|
||||
{
|
||||
pauseCooldownActive = Player.PauseCooldownActive;
|
||||
((Bindable<bool>)host.IsActive).Value = false;
|
||||
});
|
||||
AddAssert("pause cooldown active", () => pauseCooldownActive);
|
||||
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||
AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime);
|
||||
}
|
||||
|
||||
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true);
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
return new Beatmap
|
||||
{
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 30000 },
|
||||
new HitCircle { StartTime = 35000 },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
=> new TestWorkingBeatmap(beatmap, storyboard, Audio);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
config.Set(OsuSetting.ShowStoryboard, true);
|
||||
config.SetValue(OsuSetting.ShowStoryboard, true);
|
||||
|
||||
storyboard = new Storyboard();
|
||||
var backgroundLayer = storyboard.GetLayer("Background");
|
||||
|
@ -1,46 +1,41 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayer : MultiplayerTestScene
|
||||
public class TestSceneMultiplayer : ScreenTestScene
|
||||
{
|
||||
private TestMultiplayer multiplayerScreen;
|
||||
|
||||
public TestSceneMultiplayer()
|
||||
{
|
||||
var multi = new TestMultiplayer();
|
||||
AddStep("show", () =>
|
||||
{
|
||||
multiplayerScreen = new TestMultiplayer();
|
||||
|
||||
AddStep("show", () => LoadScreen(multi));
|
||||
AddUntilStep("wait for loaded", () => multi.IsLoaded);
|
||||
}
|
||||
// Needs to be added at a higher level since the multiplayer screen becomes non-current.
|
||||
Child = multiplayerScreen.Client;
|
||||
|
||||
[Test]
|
||||
public void TestOneUserJoinedMultipleTimes()
|
||||
{
|
||||
var user = new User { Id = 33 };
|
||||
LoadScreen(multiplayerScreen);
|
||||
});
|
||||
|
||||
AddRepeatStep("add user multiple times", () => Client.AddUser(user), 3);
|
||||
|
||||
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOneUserLeftMultipleTimes()
|
||||
{
|
||||
var user = new User { Id = 44 };
|
||||
|
||||
AddStep("add user", () => Client.AddUser(user));
|
||||
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
|
||||
|
||||
AddRepeatStep("remove user multiple times", () => Client.RemoveUser(user), 3);
|
||||
AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1);
|
||||
AddUntilStep("wait for loaded", () => multiplayerScreen.IsLoaded);
|
||||
}
|
||||
|
||||
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
|
||||
{
|
||||
[Cached(typeof(StatefulMultiplayerClient))]
|
||||
public readonly TestMultiplayerClient Client;
|
||||
|
||||
public TestMultiplayer()
|
||||
{
|
||||
Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager);
|
||||
}
|
||||
|
||||
protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager();
|
||||
}
|
||||
}
|
||||
|
@ -7,17 +7,23 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Rulesets.Taiko.Mods;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
@ -50,6 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
beatmaps.Add(new BeatmapInfo
|
||||
{
|
||||
Ruleset = rulesets.GetRuleset(i % 4),
|
||||
RulesetID = i % 4, // workaround for efcore 5 compatibility.
|
||||
OnlineBeatmapID = beatmapId,
|
||||
Length = length,
|
||||
BPM = bpm,
|
||||
@ -137,8 +144,30 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("mods not changed", () => SelectedMods.Value.Single() is TaikoModDoubleTime);
|
||||
}
|
||||
|
||||
[TestCase(typeof(OsuModHidden), typeof(OsuModHidden))] // Same mod.
|
||||
[TestCase(typeof(OsuModHidden), typeof(OsuModTraceable))] // Incompatible.
|
||||
public void TestAllowedModDeselectedWhenRequired(Type allowedMod, Type requiredMod)
|
||||
{
|
||||
AddStep($"select {allowedMod.ReadableName()} as allowed", () => songSelect.FreeMods.Value = new[] { (Mod)Activator.CreateInstance(allowedMod) });
|
||||
AddStep($"select {requiredMod.ReadableName()} as required", () => songSelect.Mods.Value = new[] { (Mod)Activator.CreateInstance(requiredMod) });
|
||||
|
||||
AddAssert("freemods empty", () => songSelect.FreeMods.Value.Count == 0);
|
||||
assertHasFreeModButton(allowedMod, false);
|
||||
assertHasFreeModButton(requiredMod, false);
|
||||
}
|
||||
|
||||
private void assertHasFreeModButton(Type type, bool hasButton = true)
|
||||
{
|
||||
AddAssert($"{type.ReadableName()} {(hasButton ? "displayed" : "not displayed")} in freemod overlay",
|
||||
() => songSelect.ChildrenOfType<FreeModSelectOverlay>().Single().ChildrenOfType<ModButton>().All(b => b.Mod.GetType() != type));
|
||||
}
|
||||
|
||||
private class TestMultiplayerMatchSongSelect : MultiplayerMatchSongSelect
|
||||
{
|
||||
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
|
||||
|
||||
public new Bindable<IReadOnlyList<Mod>> FreeMods => base.FreeMods;
|
||||
|
||||
public new BeatmapCarousel Carousel => base.Carousel;
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,10 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
@ -20,9 +18,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
private MultiplayerMatchSubScreen screen;
|
||||
|
||||
[Cached]
|
||||
private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker();
|
||||
|
||||
public TestSceneMultiplayerMatchSubScreen()
|
||||
: base(false)
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
|
||||
beatmaps.Import(TestResources.GetTestBeatmapForImport(true)).Wait();
|
||||
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||
|
||||
Add(beatmapTracker = new OnlinePlayBeatmapAvailablilityTracker
|
||||
{
|
||||
|
@ -1,10 +1,13 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
@ -21,15 +24,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(new Room { Name = { Value = "1" } });
|
||||
roomManager.CreateRoom(createRoom(r => r.Name.Value = "1"));
|
||||
roomManager.PartRoom();
|
||||
roomManager.CreateRoom(new Room { Name = { Value = "2" } });
|
||||
roomManager.CreateRoom(createRoom(r => r.Name.Value = "2"));
|
||||
roomManager.PartRoom();
|
||||
roomManager.ClearRooms();
|
||||
});
|
||||
});
|
||||
|
||||
AddAssert("manager polled for rooms", () => roomManager.Rooms.Count == 2);
|
||||
AddAssert("manager polled for rooms", () => ((RoomManager)roomManager).Rooms.Count == 2);
|
||||
AddAssert("initial rooms received", () => roomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
@ -40,16 +43,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(new Room());
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
roomManager.CreateRoom(new Room());
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("disconnect", () => roomContainer.Client.Disconnect());
|
||||
|
||||
AddAssert("rooms cleared", () => roomManager.Rooms.Count == 0);
|
||||
AddAssert("rooms cleared", () => ((RoomManager)roomManager).Rooms.Count == 0);
|
||||
AddAssert("initial rooms not received", () => !roomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
@ -60,9 +63,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(new Room());
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
roomManager.CreateRoom(new Room());
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
});
|
||||
});
|
||||
@ -70,7 +73,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("disconnect", () => roomContainer.Client.Disconnect());
|
||||
AddStep("connect", () => roomContainer.Client.Connect());
|
||||
|
||||
AddAssert("manager polled for rooms", () => roomManager.Rooms.Count == 2);
|
||||
AddAssert("manager polled for rooms", () => ((RoomManager)roomManager).Rooms.Count == 2);
|
||||
AddAssert("initial rooms received", () => roomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
@ -81,12 +84,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(new Room());
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.ClearRooms();
|
||||
});
|
||||
});
|
||||
|
||||
AddAssert("manager not polled for rooms", () => roomManager.Rooms.Count == 0);
|
||||
AddAssert("manager not polled for rooms", () => ((RoomManager)roomManager).Rooms.Count == 0);
|
||||
AddAssert("initial rooms not received", () => !roomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
@ -97,7 +100,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(new Room());
|
||||
roomManager.CreateRoom(createRoom());
|
||||
});
|
||||
});
|
||||
|
||||
@ -111,7 +114,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(new Room());
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
});
|
||||
});
|
||||
@ -126,7 +129,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
var r = new Room();
|
||||
var r = createRoom();
|
||||
roomManager.CreateRoom(r);
|
||||
roomManager.PartRoom();
|
||||
roomManager.JoinRoom(r);
|
||||
@ -136,6 +139,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("multiplayer room joined", () => roomContainer.Client.Room != null);
|
||||
}
|
||||
|
||||
private Room createRoom(Action<Room> initFunc = null)
|
||||
{
|
||||
var room = new Room();
|
||||
|
||||
room.Name.Value = "test room";
|
||||
room.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
|
||||
Ruleset = { Value = Ruleset.Value }
|
||||
});
|
||||
|
||||
initFunc?.Invoke(room);
|
||||
return room;
|
||||
}
|
||||
|
||||
private TestMultiplayerRoomManager createRoomManager()
|
||||
{
|
||||
Child = roomContainer = new TestMultiplayerRoomContainer
|
||||
|
@ -5,7 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Platform;
|
||||
@ -62,10 +61,6 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
RecycleLocalStorage();
|
||||
|
||||
// see MouseSettings
|
||||
var frameworkConfig = host.Dependencies.Get<FrameworkConfigManager>();
|
||||
frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity).Disabled = false;
|
||||
|
||||
CreateGame();
|
||||
});
|
||||
|
||||
@ -82,7 +77,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
// todo: this can be removed once we can run audio tracks without a device present
|
||||
// see https://github.com/ppy/osu/issues/1302
|
||||
Game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
|
||||
Game.LocalConfig.SetValue(OsuSetting.IntroSequence, IntroSequence.Circles);
|
||||
|
||||
Add(Game);
|
||||
}
|
||||
@ -136,7 +131,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
base.LoadComplete();
|
||||
API.Login("Rhythm Champion", "osu!");
|
||||
|
||||
Dependencies.Get<SessionStatics>().Set(Static.MutedAudioNotificationShownOnce, true);
|
||||
Dependencies.Get<SessionStatics>().SetValue(Static.MutedAudioNotificationShownOnce, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
PushAndConfirm(() => new TestSongSelect());
|
||||
|
||||
AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait());
|
||||
AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait());
|
||||
|
||||
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||
|
||||
@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
||||
AddStep("seek to end", () => beatmap().Track.Seek(beatmap().Track.Length));
|
||||
AddStep("seek to end", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(beatmap().Track.Length));
|
||||
AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded);
|
||||
AddStep("attempt to retry", () => results.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
||||
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != player && Game.ScreenStack.CurrentScreen is Player);
|
||||
@ -214,6 +214,50 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddAssert("Options overlay still visible", () => songSelect.BeatmapOptionsOverlay.State.Value == Visibility.Visible);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSettingsViaHotkeyFromMainMenu()
|
||||
{
|
||||
AddAssert("toolbar not displayed", () => Game.Toolbar.State.Value == Visibility.Hidden);
|
||||
|
||||
AddStep("press settings hotkey", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.Key(Key.O);
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
});
|
||||
|
||||
AddUntilStep("settings displayed", () => Game.Settings.State.Value == Visibility.Visible);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToolbarHiddenByUser()
|
||||
{
|
||||
AddStep("Enter menu", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddUntilStep("Wait for toolbar to load", () => Game.Toolbar.IsLoaded);
|
||||
|
||||
AddStep("Hide toolbar", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.Key(Key.T);
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
});
|
||||
|
||||
pushEscape();
|
||||
|
||||
AddStep("Enter menu", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddAssert("Toolbar is hidden", () => Game.Toolbar.State.Value == Visibility.Hidden);
|
||||
|
||||
AddStep("Enter song select", () =>
|
||||
{
|
||||
InputManager.Key(Key.Enter);
|
||||
InputManager.Key(Key.Enter);
|
||||
});
|
||||
|
||||
AddAssert("Toolbar is hidden", () => Game.Toolbar.State.Value == Visibility.Hidden);
|
||||
}
|
||||
|
||||
private void pushEscape() =>
|
||||
AddStep("Press escape", () => InputManager.Key(Key.Escape));
|
||||
|
||||
|
@ -15,8 +15,8 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
using (var config = new OsuConfigManager(LocalStorage))
|
||||
{
|
||||
config.Set(OsuSetting.Version, "2020.101.0");
|
||||
config.Set(OsuSetting.DisplayStarsMaximum, 10.0);
|
||||
config.SetValue(OsuSetting.Version, "2020.101.0");
|
||||
config.SetValue(OsuSetting.DisplayStarsMaximum, 10.0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
AddAssert("config has migrated value", () => Precision.AlmostEquals(Game.LocalConfig.Get<double>(OsuSetting.DisplayStarsMaximum), 10.1));
|
||||
|
||||
AddStep("set value again", () => Game.LocalConfig.Set<double>(OsuSetting.DisplayStarsMaximum, 10));
|
||||
AddStep("set value again", () => Game.LocalConfig.SetValue(OsuSetting.DisplayStarsMaximum, 10.0));
|
||||
|
||||
AddStep("force save config", () => Game.LocalConfig.Save());
|
||||
|
||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
ensureSoleilyRemoved();
|
||||
createButtonWithBeatmap(createSoleily());
|
||||
AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded);
|
||||
AddStep("import soleily", () => beatmaps.Import(TestResources.GetTestBeatmapForImport()));
|
||||
AddStep("import soleily", () => beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()));
|
||||
AddUntilStep("wait for beatmap import", () => beatmaps.GetAllUsableBeatmapSets().Any(b => b.OnlineBeatmapSetID == 241526));
|
||||
createButtonWithBeatmap(createSoleily());
|
||||
AddAssert("button state downloaded", () => downloadButton.DownloadState == DownloadState.LocallyAvailable);
|
||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Username = "flyte",
|
||||
Id = 3103765,
|
||||
IsOnline = true,
|
||||
CurrentModeRank = 1111,
|
||||
Statistics = new UserStatistics { GlobalRank = 1111 },
|
||||
Country = new Country { FlagName = "JP" },
|
||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
||||
},
|
||||
@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Username = "peppy",
|
||||
Id = 2,
|
||||
IsOnline = false,
|
||||
CurrentModeRank = 2222,
|
||||
Statistics = new UserStatistics { GlobalRank = 2222 },
|
||||
Country = new Country { FlagName = "AU" },
|
||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||
IsSupporter = true,
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
Room.RecentParticipants.Add(new User
|
||||
{
|
||||
Username = "peppy",
|
||||
CurrentModeRank = 1234,
|
||||
Statistics = new UserStatistics { GlobalRank = 1234 },
|
||||
Id = 2
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -76,7 +75,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddStep("bind user score info handler", () =>
|
||||
{
|
||||
userScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { OnlineScoreID = currentScoreId++ };
|
||||
bindHandler(3000, userScore);
|
||||
bindHandler(true, userScore);
|
||||
});
|
||||
|
||||
createResults(() => userScore);
|
||||
@ -89,7 +88,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
[Test]
|
||||
public void TestShowNullUserScoreWithDelay()
|
||||
{
|
||||
AddStep("bind delayed handler", () => bindHandler(3000));
|
||||
AddStep("bind delayed handler", () => bindHandler(true));
|
||||
|
||||
createResults();
|
||||
waitForDisplay();
|
||||
@ -103,7 +102,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
createResults();
|
||||
waitForDisplay();
|
||||
|
||||
AddStep("bind delayed handler", () => bindHandler(3000));
|
||||
AddStep("bind delayed handler", () => bindHandler(true));
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
@ -134,7 +133,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
createResults(() => userScore);
|
||||
waitForDisplay();
|
||||
|
||||
AddStep("bind delayed handler", () => bindHandler(3000));
|
||||
AddStep("bind delayed handler", () => bindHandler(true));
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
@ -169,70 +168,47 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddWaitStep("wait for display", 5);
|
||||
}
|
||||
|
||||
private void bindHandler(double delay = 0, ScoreInfo userScore = null, bool failRequests = false) => ((DummyAPIAccess)API).HandleRequest = request =>
|
||||
private void bindHandler(bool delayed = false, ScoreInfo userScore = null, bool failRequests = false) => ((DummyAPIAccess)API).HandleRequest = request =>
|
||||
{
|
||||
requestComplete = false;
|
||||
|
||||
if (failRequests)
|
||||
{
|
||||
triggerFail(request, delay);
|
||||
return;
|
||||
}
|
||||
double delay = delayed ? 3000 : 0;
|
||||
|
||||
switch (request)
|
||||
Scheduler.AddDelayed(() =>
|
||||
{
|
||||
case ShowPlaylistUserScoreRequest s:
|
||||
if (userScore == null)
|
||||
triggerFail(s, delay);
|
||||
else
|
||||
triggerSuccess(s, createUserResponse(userScore), delay);
|
||||
break;
|
||||
if (failRequests)
|
||||
{
|
||||
triggerFail(request);
|
||||
return;
|
||||
}
|
||||
|
||||
case IndexPlaylistScoresRequest i:
|
||||
triggerSuccess(i, createIndexResponse(i), delay);
|
||||
break;
|
||||
}
|
||||
switch (request)
|
||||
{
|
||||
case ShowPlaylistUserScoreRequest s:
|
||||
if (userScore == null)
|
||||
triggerFail(s);
|
||||
else
|
||||
triggerSuccess(s, createUserResponse(userScore));
|
||||
break;
|
||||
|
||||
case IndexPlaylistScoresRequest i:
|
||||
triggerSuccess(i, createIndexResponse(i));
|
||||
break;
|
||||
}
|
||||
}, delay);
|
||||
};
|
||||
|
||||
private void triggerSuccess<T>(APIRequest<T> req, T result, double delay)
|
||||
private void triggerSuccess<T>(APIRequest<T> req, T result)
|
||||
where T : class
|
||||
{
|
||||
if (delay == 0)
|
||||
success();
|
||||
else
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(delay));
|
||||
Schedule(success);
|
||||
});
|
||||
}
|
||||
|
||||
void success()
|
||||
{
|
||||
requestComplete = true;
|
||||
req.TriggerSuccess(result);
|
||||
}
|
||||
requestComplete = true;
|
||||
req.TriggerSuccess(result);
|
||||
}
|
||||
|
||||
private void triggerFail(APIRequest req, double delay)
|
||||
private void triggerFail(APIRequest req)
|
||||
{
|
||||
if (delay == 0)
|
||||
fail();
|
||||
else
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(delay));
|
||||
Schedule(fail);
|
||||
});
|
||||
}
|
||||
|
||||
void fail()
|
||||
{
|
||||
requestComplete = true;
|
||||
req.TriggerFailure(new WebException("Failed."));
|
||||
}
|
||||
requestComplete = true;
|
||||
req.TriggerFailure(new WebException("Failed."));
|
||||
}
|
||||
|
||||
private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user