mirror of
https://github.com/osukey/osukey.git
synced 2025-07-01 16:29:58 +09:00
merged in aim-base
This commit is contained in:
@ -15,7 +15,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"smoogipoo.nvika": {
|
"smoogipoo.nvika": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.3",
|
||||||
"commands": [
|
"commands": [
|
||||||
"nvika"
|
"nvika"
|
||||||
]
|
]
|
||||||
@ -33,4 +33,4 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -113,7 +113,7 @@ dotnet_style_qualification_for_event = false:warning
|
|||||||
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
|
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
|
||||||
dotnet_style_predefined_type_for_member_access = true:warning
|
dotnet_style_predefined_type_for_member_access = true:warning
|
||||||
csharp_style_var_when_type_is_apparent = true:none
|
csharp_style_var_when_type_is_apparent = true:none
|
||||||
csharp_style_var_for_built_in_types = true:none
|
csharp_style_var_for_built_in_types = false:warning
|
||||||
csharp_style_var_elsewhere = true:silent
|
csharp_style_var_elsewhere = true:silent
|
||||||
|
|
||||||
#Style - modifiers
|
#Style - modifiers
|
||||||
|
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
@ -50,6 +50,48 @@ jobs:
|
|||||||
name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
|
name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
|
||||||
path: ${{github.workspace}}/TestResults/TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx
|
path: ${{github.workspace}}/TestResults/TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx
|
||||||
|
|
||||||
|
build-only-android:
|
||||||
|
name: Build only (Android)
|
||||||
|
runs-on: windows-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install .NET 5.0.x
|
||||||
|
uses: actions/setup-dotnet@v1
|
||||||
|
with:
|
||||||
|
dotnet-version: "5.0.x"
|
||||||
|
|
||||||
|
- name: Setup MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@v1
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: msbuild osu.Android.slnf /restore /p:Configuration=Debug
|
||||||
|
|
||||||
|
build-only-ios:
|
||||||
|
# While this workflow technically *can* run, it fails as iOS builds are blocked by multiple issues.
|
||||||
|
# See https://github.com/ppy/osu-framework/issues/4677 for the details.
|
||||||
|
# The job can be unblocked once those issues are resolved and game deployments can happen again.
|
||||||
|
if: false
|
||||||
|
name: Build only (iOS)
|
||||||
|
runs-on: macos-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install .NET 5.0.x
|
||||||
|
uses: actions/setup-dotnet@v1
|
||||||
|
with:
|
||||||
|
dotnet-version: "5.0.x"
|
||||||
|
|
||||||
|
# Contrary to seemingly any other msbuild, msbuild running on macOS/Mono
|
||||||
|
# cannot accept .sln(f) files as arguments.
|
||||||
|
# Build just the main game for now.
|
||||||
|
- name: Build
|
||||||
|
run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug
|
||||||
|
|
||||||
inspect-code:
|
inspect-code:
|
||||||
name: Code Quality
|
name: Code Quality
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<EmbeddedResource Include="Resources\**\*.*" />
|
<EmbeddedResource Include="Resources\**\*.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Code Analysis">
|
<ItemGroup Label="Code Analysis">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.2" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3" PrivateAssets="All" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -51,8 +51,8 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1015.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1026.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1014.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1029.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
|
@ -20,6 +20,7 @@ namespace osu.Android
|
|||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
||||||
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osr", DataHost = "*", DataMimeType = "*/*")]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")]
|
||||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-replay")]
|
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-replay")]
|
||||||
|
@ -156,7 +156,7 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
lock (importableFiles)
|
lock (importableFiles)
|
||||||
{
|
{
|
||||||
var firstExtension = Path.GetExtension(filePaths.First());
|
string firstExtension = Path.GetExtension(filePaths.First());
|
||||||
|
|
||||||
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
Logger.Log($"Handling batch import of {importableFiles.Count} files");
|
Logger.Log($"Handling batch import of {importableFiles.Count} files");
|
||||||
|
|
||||||
var paths = importableFiles.ToArray();
|
string[] paths = importableFiles.ToArray();
|
||||||
importableFiles.Clear();
|
importableFiles.Clear();
|
||||||
|
|
||||||
Task.Factory.StartNew(() => Import(paths), TaskCreationOptions.LongRunning);
|
Task.Factory.StartNew(() => Import(paths), TaskCreationOptions.LongRunning);
|
||||||
|
@ -22,17 +22,17 @@ namespace osu.Desktop
|
|||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
// Back up the cwd before DesktopGameHost changes it
|
// Back up the cwd before DesktopGameHost changes it
|
||||||
var cwd = Environment.CurrentDirectory;
|
string cwd = Environment.CurrentDirectory;
|
||||||
|
|
||||||
string gameName = base_game_name;
|
string gameName = base_game_name;
|
||||||
bool tournamentClient = false;
|
bool tournamentClient = false;
|
||||||
|
|
||||||
foreach (var arg in args)
|
foreach (string arg in args)
|
||||||
{
|
{
|
||||||
var split = arg.Split('=');
|
string[] split = arg.Split('=');
|
||||||
|
|
||||||
var key = split[0];
|
string key = split[0];
|
||||||
var val = split.Length > 1 ? split[1] : string.Empty;
|
string val = split.Length > 1 ? split[1] : string.Empty;
|
||||||
|
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
@ -62,7 +62,7 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
var importer = new ArchiveImportIPCChannel(host);
|
var importer = new ArchiveImportIPCChannel(host);
|
||||||
|
|
||||||
foreach (var file in args)
|
foreach (string file in args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"Importing {0}", file);
|
Console.WriteLine(@"Importing {0}", file);
|
||||||
if (!importer.ImportAsync(Path.GetFullPath(file, cwd)).Wait(3000))
|
if (!importer.ImportAsync(Path.GetFullPath(file, cwd)).Wait(3000))
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Catch.Edit;
|
||||||
|
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||||
|
{
|
||||||
|
public class TestSceneCatchDistanceSnapGrid : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private readonly ManualClock manualClock = new ManualClock();
|
||||||
|
|
||||||
|
[Cached(typeof(Playfield))]
|
||||||
|
private readonly CatchPlayfield playfield;
|
||||||
|
|
||||||
|
private ScrollingHitObjectContainer hitObjectContainer => playfield.HitObjectContainer;
|
||||||
|
|
||||||
|
private readonly CatchDistanceSnapGrid distanceGrid;
|
||||||
|
|
||||||
|
private readonly FruitOutline fruitOutline;
|
||||||
|
|
||||||
|
private readonly Fruit fruit = new Fruit();
|
||||||
|
|
||||||
|
public TestSceneCatchDistanceSnapGrid()
|
||||||
|
{
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = 500,
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new ScrollingTestContainer(ScrollingDirection.Down)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = playfield = new CatchPlayfield(new BeatmapDifficulty())
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Clock = new FramedClock(manualClock)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
distanceGrid = new CatchDistanceSnapGrid(new double[] { 0, -1, 1 }),
|
||||||
|
fruitOutline = new FruitOutline()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
distanceGrid.StartTime = 100;
|
||||||
|
distanceGrid.StartX = 250;
|
||||||
|
|
||||||
|
Vector2 screenSpacePosition = InputManager.CurrentState.Mouse.Position;
|
||||||
|
|
||||||
|
var result = distanceGrid.GetSnappedPosition(screenSpacePosition);
|
||||||
|
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
fruit.OriginalX = hitObjectContainer.ToLocalSpace(result.ScreenSpacePosition).X;
|
||||||
|
|
||||||
|
if (result.Time != null)
|
||||||
|
fruit.StartTime = result.Time.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fruitOutline.Position = CatchHitObjectUtils.GetStartPosition(hitObjectContainer, fruit);
|
||||||
|
fruitOutline.UpdateFrom(fruit);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnScroll(ScrollEvent e)
|
||||||
|
{
|
||||||
|
manualClock.CurrentTime -= e.ScrollDelta.Y * 50;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
|
|
||||||
AddAssert("correct outline count", () =>
|
AddAssert("correct outline count", () =>
|
||||||
{
|
{
|
||||||
var expected = hitObject.NestedHitObjects.Count(h => !(h is TinyDroplet));
|
int expected = hitObject.NestedHitObjects.Count(h => !(h is TinyDroplet));
|
||||||
return this.ChildrenOfType<FruitOutline>().Count() == expected;
|
return this.ChildrenOfType<FruitOutline>().Count() == expected;
|
||||||
});
|
});
|
||||||
AddAssert("correct vertex piece count", () =>
|
AddAssert("correct vertex piece count", () =>
|
||||||
|
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestCatcherCatchWidth()
|
public void TestCatcherCatchWidth()
|
||||||
{
|
{
|
||||||
var halfWidth = Catcher.CalculateCatchWidth(new BeatmapDifficulty { CircleSize = 0 }) / 2;
|
float halfWidth = Catcher.CalculateCatchWidth(new BeatmapDifficulty { CircleSize = 0 }) / 2;
|
||||||
AddStep("catch fruit", () =>
|
AddStep("catch fruit", () =>
|
||||||
{
|
{
|
||||||
attemptCatch(new Fruit { X = -halfWidth + 1 });
|
attemptCatch(new Fruit { X = -halfWidth + 1 });
|
||||||
@ -237,7 +237,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
private void attemptCatch(Func<CatchHitObject> hitObject, int count)
|
private void attemptCatch(Func<CatchHitObject> hitObject, int count)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
attemptCatch(hitObject(), out _, out _);
|
attemptCatch(hitObject(), out _, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
private void spawnJuiceStream(bool hit = false)
|
private void spawnJuiceStream(bool hit = false)
|
||||||
{
|
{
|
||||||
var xCoords = getXCoords(hit);
|
float xCoords = getXCoords(hit);
|
||||||
|
|
||||||
var juice = new JuiceStream
|
var juice = new JuiceStream
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
int thisDirection = nextObject.EffectiveX > currentObject.EffectiveX ? 1 : -1;
|
int thisDirection = nextObject.EffectiveX > currentObject.EffectiveX ? 1 : -1;
|
||||||
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
|
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
|
||||||
double distanceToNext = Math.Abs(nextObject.EffectiveX - currentObject.EffectiveX) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
double distanceToNext = Math.Abs(nextObject.EffectiveX - currentObject.EffectiveX) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
||||||
float distanceToHyper = (float)(timeToNext * Catcher.BASE_SPEED - distanceToNext);
|
float distanceToHyper = (float)(timeToNext * Catcher.BASE_DASH_SPEED - distanceToNext);
|
||||||
|
|
||||||
if (distanceToHyper < 0)
|
if (distanceToHyper < 0)
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
: base(hitObject, lastObject, clockRate)
|
: base(hitObject, lastObject, clockRate)
|
||||||
{
|
{
|
||||||
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||||
var scalingFactor = normalized_hitobject_radius / halfCatcherWidth;
|
float scalingFactor = normalized_hitobject_radius / halfCatcherWidth;
|
||||||
|
|
||||||
NormalizedPosition = BaseObject.EffectiveX * scalingFactor;
|
NormalizedPosition = BaseObject.EffectiveX * scalingFactor;
|
||||||
LastNormalizedPosition = LastObject.EffectiveX * scalingFactor;
|
LastNormalizedPosition = LastObject.EffectiveX * scalingFactor;
|
||||||
|
141
osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs
Normal file
141
osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Lines;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Edit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The guide lines used in the osu!catch editor to compose patterns that can be caught with constant speed.
|
||||||
|
/// Currently, only forward placement (an object is snapped based on the previous object, not the opposite) is supported.
|
||||||
|
/// </summary>
|
||||||
|
public class CatchDistanceSnapGrid : CompositeDrawable
|
||||||
|
{
|
||||||
|
public double StartTime { get; set; }
|
||||||
|
|
||||||
|
public float StartX { get; set; }
|
||||||
|
|
||||||
|
private const double max_vertical_line_length_in_time = CatchPlayfield.WIDTH / Catcher.BASE_WALK_SPEED;
|
||||||
|
|
||||||
|
private readonly double[] velocities;
|
||||||
|
|
||||||
|
private readonly List<Path> verticalPaths = new List<Path>();
|
||||||
|
|
||||||
|
private readonly List<Vector2[]> verticalLineVertices = new List<Vector2[]>();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private Playfield playfield { get; set; }
|
||||||
|
|
||||||
|
private ScrollingHitObjectContainer hitObjectContainer => (ScrollingHitObjectContainer)playfield.HitObjectContainer;
|
||||||
|
|
||||||
|
public CatchDistanceSnapGrid(double[] velocities)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Anchor = Anchor.BottomLeft;
|
||||||
|
|
||||||
|
this.velocities = velocities;
|
||||||
|
|
||||||
|
for (int i = 0; i < velocities.Length; i++)
|
||||||
|
{
|
||||||
|
verticalPaths.Add(new SmoothPath
|
||||||
|
{
|
||||||
|
PathRadius = 2,
|
||||||
|
Alpha = 0.5f,
|
||||||
|
});
|
||||||
|
|
||||||
|
verticalLineVertices.Add(new[] { Vector2.Zero, Vector2.Zero });
|
||||||
|
}
|
||||||
|
|
||||||
|
AddRangeInternal(verticalPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
double currentTime = hitObjectContainer.Time.Current;
|
||||||
|
|
||||||
|
for (int i = 0; i < velocities.Length; i++)
|
||||||
|
{
|
||||||
|
double velocity = velocities[i];
|
||||||
|
|
||||||
|
// The line ends at the top of the playfield.
|
||||||
|
double endTime = hitObjectContainer.TimeAtPosition(-hitObjectContainer.DrawHeight, currentTime);
|
||||||
|
|
||||||
|
// Non-vertical lines are cut at the sides of the playfield.
|
||||||
|
// Vertical lines are cut at some reasonable length.
|
||||||
|
if (velocity > 0)
|
||||||
|
endTime = Math.Min(endTime, StartTime + (CatchPlayfield.WIDTH - StartX) / velocity);
|
||||||
|
else if (velocity < 0)
|
||||||
|
endTime = Math.Min(endTime, StartTime + StartX / -velocity);
|
||||||
|
else
|
||||||
|
endTime = Math.Min(endTime, StartTime + max_vertical_line_length_in_time);
|
||||||
|
|
||||||
|
Vector2[] lineVertices = verticalLineVertices[i];
|
||||||
|
lineVertices[0] = calculatePosition(velocity, StartTime);
|
||||||
|
lineVertices[1] = calculatePosition(velocity, endTime);
|
||||||
|
|
||||||
|
var verticalPath = verticalPaths[i];
|
||||||
|
verticalPath.Vertices = verticalLineVertices[i];
|
||||||
|
verticalPath.OriginPosition = verticalPath.PositionInBoundingBox(Vector2.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 calculatePosition(double velocity, double time)
|
||||||
|
{
|
||||||
|
// Don't draw inverted lines.
|
||||||
|
time = Math.Max(time, StartTime);
|
||||||
|
|
||||||
|
float x = StartX + (float)((time - StartTime) * velocity);
|
||||||
|
float y = hitObjectContainer.PositionAtTime(time, currentTime);
|
||||||
|
return new Vector2(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
public SnapResult GetSnappedPosition(Vector2 screenSpacePosition)
|
||||||
|
{
|
||||||
|
double time = hitObjectContainer.TimeAtScreenSpacePosition(screenSpacePosition);
|
||||||
|
|
||||||
|
// If the cursor is below the distance snap grid, snap to the origin.
|
||||||
|
// Not returning `null` to retain the continuous snapping behavior when the cursor is slightly below the origin.
|
||||||
|
// This behavior is not currently visible in the editor because editor chooses the snap start time based on the mouse position.
|
||||||
|
if (time <= StartTime)
|
||||||
|
{
|
||||||
|
float y = hitObjectContainer.PositionAtTime(StartTime);
|
||||||
|
Vector2 originPosition = hitObjectContainer.ToScreenSpace(new Vector2(StartX, y));
|
||||||
|
return new SnapResult(originPosition, StartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return enumerateSnappingCandidates(time)
|
||||||
|
.OrderBy(pos => Vector2.DistanceSquared(screenSpacePosition, pos.ScreenSpacePosition))
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<SnapResult> enumerateSnappingCandidates(double time)
|
||||||
|
{
|
||||||
|
float y = hitObjectContainer.PositionAtTime(time);
|
||||||
|
|
||||||
|
foreach (double velocity in velocities)
|
||||||
|
{
|
||||||
|
float x = (float)(StartX + (time - StartTime) * velocity);
|
||||||
|
Vector2 screenSpacePosition = hitObjectContainer.ToScreenSpace(new Vector2(x, y + hitObjectContainer.DrawHeight));
|
||||||
|
yield return new SnapResult(screenSpacePosition, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,23 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -17,6 +26,14 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
{
|
{
|
||||||
public class CatchHitObjectComposer : HitObjectComposer<CatchHitObject>
|
public class CatchHitObjectComposer : HitObjectComposer<CatchHitObject>
|
||||||
{
|
{
|
||||||
|
private const float distance_snap_radius = 50;
|
||||||
|
|
||||||
|
private CatchDistanceSnapGrid distanceSnapGrid;
|
||||||
|
|
||||||
|
private readonly Bindable<TernaryState> distanceSnapToggle = new Bindable<TernaryState>();
|
||||||
|
|
||||||
|
private InputManager inputManager;
|
||||||
|
|
||||||
public CatchHitObjectComposer(CatchRuleset ruleset)
|
public CatchHitObjectComposer(CatchRuleset ruleset)
|
||||||
: base(ruleset)
|
: base(ruleset)
|
||||||
{
|
{
|
||||||
@ -30,6 +47,27 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners }
|
PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
LayerBelowRuleset.Add(distanceSnapGrid = new CatchDistanceSnapGrid(new[]
|
||||||
|
{
|
||||||
|
0.0,
|
||||||
|
Catcher.BASE_DASH_SPEED, -Catcher.BASE_DASH_SPEED,
|
||||||
|
Catcher.BASE_WALK_SPEED, -Catcher.BASE_WALK_SPEED,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
inputManager = GetContainingInputManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
updateDistanceSnapGrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DrawableRuleset<CatchHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null) =>
|
protected override DrawableRuleset<CatchHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null) =>
|
||||||
@ -42,14 +80,95 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
new BananaShowerCompositionTool()
|
new BananaShowerCompositionTool()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected override IEnumerable<TernaryButton> CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[]
|
||||||
|
{
|
||||||
|
new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler })
|
||||||
|
});
|
||||||
|
|
||||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||||
{
|
{
|
||||||
var result = base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
var result = base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
||||||
// TODO: implement position snap
|
|
||||||
result.ScreenSpacePosition.X = screenSpacePosition.X;
|
result.ScreenSpacePosition.X = screenSpacePosition.X;
|
||||||
|
|
||||||
|
if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult &&
|
||||||
|
Vector2.Distance(snapResult.ScreenSpacePosition, result.ScreenSpacePosition) < distance_snap_radius)
|
||||||
|
{
|
||||||
|
result = snapResult;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new CatchBlueprintContainer(this);
|
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new CatchBlueprintContainer(this);
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private PalpableCatchHitObject getLastSnappableHitObject(double time)
|
||||||
|
{
|
||||||
|
var hitObject = EditorBeatmap.HitObjects.OfType<CatchHitObject>().LastOrDefault(h => h.GetEndTime() < time && !(h is BananaShower));
|
||||||
|
|
||||||
|
switch (hitObject)
|
||||||
|
{
|
||||||
|
case Fruit fruit:
|
||||||
|
return fruit;
|
||||||
|
|
||||||
|
case JuiceStream juiceStream:
|
||||||
|
return juiceStream.NestedHitObjects.OfType<PalpableCatchHitObject>().LastOrDefault(h => !(h is TinyDroplet));
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private PalpableCatchHitObject getDistanceSnapGridSourceHitObject()
|
||||||
|
{
|
||||||
|
switch (BlueprintContainer.CurrentTool)
|
||||||
|
{
|
||||||
|
case SelectTool _:
|
||||||
|
if (EditorBeatmap.SelectedHitObjects.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
double minTime = EditorBeatmap.SelectedHitObjects.Min(hitObject => hitObject.StartTime);
|
||||||
|
return getLastSnappableHitObject(minTime);
|
||||||
|
|
||||||
|
case FruitCompositionTool _:
|
||||||
|
case JuiceStreamCompositionTool _:
|
||||||
|
if (!CursorInPlacementArea)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (EditorBeatmap.PlacementObject.Value is JuiceStream)
|
||||||
|
{
|
||||||
|
// Juice stream path is not subject to snapping.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
double timeAtCursor = ((CatchPlayfield)Playfield).TimeAtScreenSpacePosition(inputManager.CurrentState.Mouse.Position);
|
||||||
|
return getLastSnappableHitObject(timeAtCursor);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDistanceSnapGrid()
|
||||||
|
{
|
||||||
|
if (distanceSnapToggle.Value != TernaryState.True)
|
||||||
|
{
|
||||||
|
distanceSnapGrid.Hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceHitObject = getDistanceSnapGridSourceHitObject();
|
||||||
|
|
||||||
|
if (sourceHitObject == null)
|
||||||
|
{
|
||||||
|
distanceSnapGrid.Hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
distanceSnapGrid.Show();
|
||||||
|
distanceSnapGrid.StartTime = sourceHitObject.GetEndTime();
|
||||||
|
distanceSnapGrid.StartX = sourceHitObject.EffectiveX;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } },
|
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad" } },
|
||||||
Replay = new CatchAutoGenerator(beatmap).Generate(),
|
Replay = new CatchAutoGenerator(beatmap).Generate(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } },
|
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad" } },
|
||||||
Replay = new CatchAutoGenerator(beatmap).Generate(),
|
Replay = new CatchAutoGenerator(beatmap).Generate(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
var hitObject = drawable.HitObject;
|
var hitObject = drawable.HitObject;
|
||||||
|
|
||||||
var offset = hitObject.TimePreempt * fade_out_offset_multiplier;
|
double offset = hitObject.TimePreempt * fade_out_offset_multiplier;
|
||||||
var duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier;
|
double duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier;
|
||||||
|
|
||||||
using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset))
|
using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset))
|
||||||
drawable.FadeOut(duration);
|
drawable.FadeOut(duration);
|
||||||
|
@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public double Distance => Path.Distance;
|
public double Distance => Path.Distance;
|
||||||
|
|
||||||
public List<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
|
public IList<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
|
||||||
|
|
||||||
public double? LegacyLastTickOffset { get; set; }
|
public double? LegacyLastTickOffset { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,6 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
if (Beatmap.HitObjects.Count == 0)
|
if (Beatmap.HitObjects.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// todo: add support for HT DT
|
|
||||||
const double dash_speed = Catcher.BASE_SPEED;
|
|
||||||
const double movement_speed = dash_speed / 2;
|
|
||||||
float lastPosition = CatchPlayfield.CENTER_X;
|
float lastPosition = CatchPlayfield.CENTER_X;
|
||||||
double lastTime = 0;
|
double lastTime = 0;
|
||||||
|
|
||||||
@ -47,8 +44,8 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
// The case where positionChange > 0 and timeAvailable == 0 results in PositiveInfinity which provides expected beheaviour.
|
// The case where positionChange > 0 and timeAvailable == 0 results in PositiveInfinity which provides expected beheaviour.
|
||||||
double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable;
|
double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable;
|
||||||
|
|
||||||
bool dashRequired = speedRequired > movement_speed;
|
bool dashRequired = speedRequired > Catcher.BASE_WALK_SPEED;
|
||||||
bool impossibleJump = speedRequired > movement_speed * 2;
|
bool impossibleJump = speedRequired > Catcher.BASE_DASH_SPEED;
|
||||||
|
|
||||||
// todo: get correct catcher size, based on difficulty CS.
|
// todo: get correct catcher size, based on difficulty CS.
|
||||||
const float catcher_width_half = Catcher.BASE_SIZE * 0.3f * 0.5f;
|
const float catcher_width_half = Catcher.BASE_SIZE * 0.3f * 0.5f;
|
||||||
@ -73,7 +70,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
else if (dashRequired)
|
else if (dashRequired)
|
||||||
{
|
{
|
||||||
// we do a movement in two parts - the dash part then the normal part...
|
// we do a movement in two parts - the dash part then the normal part...
|
||||||
double timeAtNormalSpeed = positionChange / movement_speed;
|
double timeAtNormalSpeed = positionChange / Catcher.BASE_WALK_SPEED;
|
||||||
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
||||||
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
||||||
|
|
||||||
@ -86,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double timeBefore = positionChange / movement_speed;
|
double timeBefore = positionChange / Catcher.BASE_WALK_SPEED;
|
||||||
|
|
||||||
addFrame(h.StartTime - timeBefore, lastPosition);
|
addFrame(h.StartTime - timeBefore, lastPosition);
|
||||||
addFrame(h.StartTime, h.EffectiveX);
|
addFrame(h.StartTime, h.EffectiveX);
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
|
|
||||||
public override void CollectPendingInputs(List<IInput> inputs)
|
public override void CollectPendingInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
var position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
float position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
||||||
|
|
||||||
inputs.Add(new CatchReplayState
|
inputs.Add(new CatchReplayState
|
||||||
{
|
{
|
||||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
case CatchSkinComponents.Catcher:
|
case CatchSkinComponents.Catcher:
|
||||||
var version = GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value ?? 1;
|
decimal version = GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value ?? 1;
|
||||||
|
|
||||||
if (version < 2.3m)
|
if (version < 2.3m)
|
||||||
{
|
{
|
||||||
|
@ -57,14 +57,19 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
public bool CatchFruitOnPlate { get; set; } = true;
|
public bool CatchFruitOnPlate { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
/// The speed of the catcher when the catcher is dashing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const double BASE_SPEED = 1.0;
|
public const double BASE_DASH_SPEED = 1.0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current speed of the catcher.
|
/// The speed of the catcher when the catcher is not dashing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double Speed => (Dashing ? 1 : 0.5) * BASE_SPEED * hyperDashModifier;
|
public const double BASE_WALK_SPEED = 0.5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current speed of the catcher with the hyper-dash modifier applied.
|
||||||
|
/// </summary>
|
||||||
|
public double Speed => (Dashing ? BASE_DASH_SPEED : BASE_WALK_SPEED) * hyperDashModifier;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount by which caught fruit should be scaled down to fit on the plate.
|
/// The amount by which caught fruit should be scaled down to fit on the plate.
|
||||||
@ -226,11 +231,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
if (result.IsHit && hitObject.HyperDash)
|
if (result.IsHit && hitObject.HyperDash)
|
||||||
{
|
{
|
||||||
var target = hitObject.HyperDashTarget;
|
var target = hitObject.HyperDashTarget;
|
||||||
var timeDifference = target.StartTime - hitObject.StartTime;
|
double timeDifference = target.StartTime - hitObject.StartTime;
|
||||||
double positionDifference = target.EffectiveX - X;
|
double positionDifference = target.EffectiveX - X;
|
||||||
var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
|
double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
|
||||||
|
|
||||||
SetHyperDashState(Math.Abs(velocity), target.EffectiveX);
|
SetHyperDashState(Math.Abs(velocity) / BASE_DASH_SPEED, target.EffectiveX);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
SetHyperDashState();
|
SetHyperDashState();
|
||||||
@ -266,7 +271,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyper-dashing.</param>
|
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyper-dashing.</param>
|
||||||
public void SetHyperDashState(double modifier = 1, float targetPosition = -1)
|
public void SetHyperDashState(double modifier = 1, float targetPosition = -1)
|
||||||
{
|
{
|
||||||
var wasHyperDashing = HyperDashing;
|
bool wasHyperDashing = HyperDashing;
|
||||||
|
|
||||||
if (modifier <= 1 || X == targetPosition)
|
if (modifier <= 1 || X == targetPosition)
|
||||||
{
|
{
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
|
|
||||||
protected override SnapResult SnapForBlueprint(PlacementBlueprint blueprint)
|
protected override SnapResult SnapForBlueprint(PlacementBlueprint blueprint)
|
||||||
{
|
{
|
||||||
var time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position);
|
double time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position);
|
||||||
var pos = column.ScreenSpacePositionAtTime(time);
|
var pos = column.ScreenSpacePositionAtTime(time);
|
||||||
|
|
||||||
return new SnapResult(pos, time, column);
|
return new SnapResult(pos, time, column);
|
||||||
|
@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
public int CompareTo(ConvertValue other)
|
public int CompareTo(ConvertValue other)
|
||||||
{
|
{
|
||||||
var result = StartTime.CompareTo(other.StartTime);
|
int result = StartTime.CompareTo(other.StartTime);
|
||||||
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
return result;
|
return result;
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
private IList<string> getSampleNames(IList<HitSampleInfo> hitSampleInfo)
|
private IList<string> getSampleNames(IList<HitSampleInfo> hitSampleInfo)
|
||||||
=> hitSampleInfo.Select(sample => sample.LookupNames.First()).ToList();
|
=> hitSampleInfo.Select(sample => sample.LookupNames.First()).ToList();
|
||||||
|
|
||||||
private IList<IList<string>> getNodeSampleNames(List<IList<HitSampleInfo>> hitSampleInfo)
|
private IList<IList<string>> getNodeSampleNames(IList<IList<HitSampleInfo>> hitSampleInfo)
|
||||||
=> hitSampleInfo?.Select(getSampleNames)
|
=> hitSampleInfo?.Select(getSampleNames)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
private IEnumerable<ColumnType> getResults(StageDefinition definition)
|
private IEnumerable<ColumnType> getResults(StageDefinition definition)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < definition.Columns; i++)
|
for (int i = 0; i < definition.Columns; i++)
|
||||||
yield return definition.GetTypeOfColumn(i);
|
yield return definition.GetTypeOfColumn(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -42,8 +42,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
{
|
{
|
||||||
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
|
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
|
||||||
|
|
||||||
var roundedCircleSize = Math.Round(beatmap.Difficulty.CircleSize);
|
double roundedCircleSize = Math.Round(beatmap.Difficulty.CircleSize);
|
||||||
var roundedOverallDifficulty = Math.Round(beatmap.Difficulty.OverallDifficulty);
|
double roundedOverallDifficulty = Math.Round(beatmap.Difficulty.OverallDifficulty);
|
||||||
|
|
||||||
if (IsForCurrentRuleset)
|
if (IsForCurrentRuleset)
|
||||||
{
|
{
|
||||||
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
public static int GetColumnCountForNonConvert(BeatmapInfo beatmapInfo)
|
public static int GetColumnCountForNonConvert(BeatmapInfo beatmapInfo)
|
||||||
{
|
{
|
||||||
var roundedCircleSize = Math.Round(beatmapInfo.BaseDifficulty.CircleSize);
|
double roundedCircleSize = Math.Round(beatmapInfo.BaseDifficulty.CircleSize);
|
||||||
return (int)Math.Max(1, roundedCircleSize);
|
return (int)Math.Max(1, roundedCircleSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,12 +488,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// Retrieves the list of node samples that occur at time greater than or equal to <paramref name="time"/>.
|
/// Retrieves the list of node samples that occur at time greater than or equal to <paramref name="time"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to retrieve node samples at.</param>
|
/// <param name="time">The time to retrieve node samples at.</param>
|
||||||
private List<IList<HitSampleInfo>> nodeSamplesAt(int time)
|
private IList<IList<HitSampleInfo>> nodeSamplesAt(int time)
|
||||||
{
|
{
|
||||||
if (!(HitObject is IHasPathWithRepeats curveData))
|
if (!(HitObject is IHasPathWithRepeats curveData))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var index = SegmentDuration == 0 ? 0 : (time - StartTime) / SegmentDuration;
|
int index = SegmentDuration == 0 ? 0 : (time - StartTime) / SegmentDuration;
|
||||||
|
|
||||||
// avoid slicing the list & creating copies, if at all possible.
|
// avoid slicing the list & creating copies, if at all possible.
|
||||||
return index == 0 ? curveData.NodeSamples : curveData.NodeSamples.Skip(index).ToList();
|
return index == 0 ? curveData.NodeSamples : curveData.NodeSamples.Skip(index).ToList();
|
||||||
|
@ -302,7 +302,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out var addToCentre);
|
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out bool addToCentre);
|
||||||
|
|
||||||
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
|
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
|
||||||
int nextColumn = GetRandomColumn(upperBound: columnLimit);
|
int nextColumn = GetRandomColumn(upperBound: columnLimit);
|
||||||
|
@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
|||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
{
|
{
|
||||||
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
||||||
var endTime = maniaCurrent.EndTime;
|
double endTime = maniaCurrent.EndTime;
|
||||||
var column = maniaCurrent.BaseObject.Column;
|
int column = maniaCurrent.BaseObject.Column;
|
||||||
|
|
||||||
double holdFactor = 1.0; // Factor to all additional strains in case something else is held
|
double holdFactor = 1.0; // Factor to all additional strains in case something else is held
|
||||||
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
|
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
|
||||||
|
@ -316,7 +316,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
case PlayfieldType.Dual:
|
case PlayfieldType.Dual:
|
||||||
{
|
{
|
||||||
var keys = getDualStageKeyCount(variant);
|
int keys = getDualStageKeyCount(variant);
|
||||||
return $"{keys}K + {keys}K";
|
return $"{keys}K + {keys}K";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,9 +62,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
|||||||
|
|
||||||
if (i < j)
|
if (i < j)
|
||||||
{
|
{
|
||||||
T key = keys[i];
|
(keys[i], keys[j]) = (keys[j], keys[i]);
|
||||||
keys[i] = keys[j];
|
|
||||||
keys[j] = key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
@ -122,7 +120,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
|||||||
|
|
||||||
while (i <= n / 2)
|
while (i <= n / 2)
|
||||||
{
|
{
|
||||||
var child = 2 * i;
|
int child = 2 * i;
|
||||||
|
|
||||||
if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
|
if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
|
||||||
{
|
{
|
||||||
@ -142,11 +140,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
|||||||
private static void swap(T[] a, int i, int j)
|
private static void swap(T[] a, int i, int j)
|
||||||
{
|
{
|
||||||
if (i != j)
|
if (i != j)
|
||||||
{
|
(a[i], a[j]) = (a[j], a[i]);
|
||||||
T t = a[i];
|
|
||||||
a[i] = a[j];
|
|
||||||
a[j] = t;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void swapIfGreater(T[] keys, IComparer<T> comparer, int a, int b)
|
private static void swapIfGreater(T[] keys, IComparer<T> comparer, int a, int b)
|
||||||
@ -154,11 +148,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
|||||||
if (a != b)
|
if (a != b)
|
||||||
{
|
{
|
||||||
if (comparer.Compare(keys[a], keys[b]) > 0)
|
if (comparer.Compare(keys[a], keys[b]) > 0)
|
||||||
{
|
(keys[a], keys[b]) = (keys[b], keys[a]);
|
||||||
T key = keys[a];
|
|
||||||
keys[a] = keys[b];
|
|
||||||
keys[b] = key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } },
|
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus" } },
|
||||||
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
|
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } },
|
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus" } },
|
||||||
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
|
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
int availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||||
|
|
||||||
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = availableColumns - 1 - h.Column);
|
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = availableColumns - 1 - h.Column);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
Seed.Value ??= RNG.Next();
|
Seed.Value ??= RNG.Next();
|
||||||
var rng = new Random((int)Seed.Value);
|
var rng = new Random((int)Seed.Value);
|
||||||
|
|
||||||
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
int availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||||
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => rng.Next()).ToList();
|
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => rng.Next()).ToList();
|
||||||
|
|
||||||
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
|
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
|
||||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
if (Time.Current < HitObject.StartTime)
|
if (Time.Current < HitObject.StartTime)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var startTime = holdStartTime?.Invoke();
|
double? startTime = holdStartTime?.Invoke();
|
||||||
|
|
||||||
if (startTime == null || startTime > HitObject.StartTime)
|
if (startTime == null || startTime > HitObject.StartTime)
|
||||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IList<HitSampleInfo>> NodeSamples { get; set; }
|
public IList<IList<HitSampleInfo>> NodeSamples { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The head note of the hold.
|
/// The head note of the hold.
|
||||||
|
@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
{
|
{
|
||||||
var currentObject = Beatmap.HitObjects[i];
|
var currentObject = Beatmap.HitObjects[i];
|
||||||
var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button
|
var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button
|
||||||
var releaseTime = calculateReleaseTime(currentObject, nextObjectInColumn);
|
double releaseTime = calculateReleaseTime(currentObject, nextObjectInColumn);
|
||||||
|
|
||||||
yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column };
|
yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column };
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
float rightLineWidth = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.RightLineWidth, columnIndex)?.Value ?? 1;
|
float rightLineWidth = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.RightLineWidth, columnIndex)?.Value ?? 1;
|
||||||
|
|
||||||
bool hasLeftLine = leftLineWidth > 0;
|
bool hasLeftLine = leftLineWidth > 0;
|
||||||
bool hasRightLine = rightLineWidth > 0 && skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
|
bool hasRightLine = rightLineWidth > 0 && skin.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
|
||||||
|| isLastColumn;
|
|| isLastColumn;
|
||||||
|
|
||||||
Color4 lineColour = skin.GetManiaSkinConfig<Color4>(LegacyManiaSkinConfigurationLookups.ColumnLineColour, columnIndex)?.Value ?? Color4.White;
|
Color4 lineColour = skin.GetManiaSkinConfig<Color4>(LegacyManiaSkinConfigurationLookups.ColumnLineColour, columnIndex)?.Value ?? Color4.White;
|
||||||
|
@ -63,10 +63,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
this.beatmap = (ManiaBeatmap)beatmap;
|
this.beatmap = (ManiaBeatmap)beatmap;
|
||||||
|
|
||||||
isLegacySkin = new Lazy<bool>(() => GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null);
|
isLegacySkin = new Lazy<bool>(() => GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version) != null);
|
||||||
hasKeyTexture = new Lazy<bool>(() =>
|
hasKeyTexture = new Lazy<bool>(() =>
|
||||||
{
|
{
|
||||||
var keyImage = this.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1";
|
string keyImage = this.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1";
|
||||||
return this.GetAnimation(keyImage, true, true) != null;
|
return this.GetAnimation(keyImage, true, true) != null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,42 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
PassCondition = checkSomeHit
|
PassCondition = checkSomeHit
|
||||||
});
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestApproachCirclesOnly() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModHidden { OnlyFadeApproachCircles = { Value = true } },
|
||||||
|
Autoplay = true,
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject>
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Position = new Vector2(206, 142)
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 2000,
|
||||||
|
Position = new Vector2(306, 142)
|
||||||
|
},
|
||||||
|
new Slider
|
||||||
|
{
|
||||||
|
StartTime = 3000,
|
||||||
|
Position = new Vector2(156, 242),
|
||||||
|
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(200, 0), })
|
||||||
|
},
|
||||||
|
new Spinner
|
||||||
|
{
|
||||||
|
Position = new Vector2(256, 192),
|
||||||
|
StartTime = 7000,
|
||||||
|
EndTime = 9000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PassCondition = checkSomeHit
|
||||||
|
});
|
||||||
|
|
||||||
private bool checkSomeHit() => Player.ScoreProcessor.JudgedHits >= 4;
|
private bool checkSomeHit() => Player.ScoreProcessor.JudgedHits >= 4;
|
||||||
|
|
||||||
private bool objectWithIncreasedVisibilityHasIndex(int index)
|
private bool objectWithIncreasedVisibilityHasIndex(int index)
|
||||||
|
150
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs
Normal file
150
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||||
|
{
|
||||||
|
public class TestSceneOsuModNoScope : OsuModTestScene
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestVisibleDuringBreak()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModNoScope
|
||||||
|
{
|
||||||
|
HiddenComboCount = { Value = 0 },
|
||||||
|
},
|
||||||
|
Autoplay = true,
|
||||||
|
PassCondition = () => true,
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject>
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = new Vector2(300, 192),
|
||||||
|
StartTime = 1000,
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = new Vector2(300, 192),
|
||||||
|
StartTime = 5000,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Breaks = new List<BreakPeriod>
|
||||||
|
{
|
||||||
|
new BreakPeriod(2000, 4000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0));
|
||||||
|
AddUntilStep("wait for start of break", isBreak);
|
||||||
|
AddUntilStep("wait for cursor to show", () => cursorAlphaAlmostEquals(1));
|
||||||
|
AddUntilStep("wait for end of break", () => !isBreak());
|
||||||
|
AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestVisibleDuringSpinner()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModNoScope
|
||||||
|
{
|
||||||
|
HiddenComboCount = { Value = 0 },
|
||||||
|
},
|
||||||
|
Autoplay = true,
|
||||||
|
PassCondition = () => true,
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject>
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = new Vector2(300, 192),
|
||||||
|
StartTime = 1000,
|
||||||
|
},
|
||||||
|
new Spinner
|
||||||
|
{
|
||||||
|
Position = new Vector2(256, 192),
|
||||||
|
StartTime = 2000,
|
||||||
|
Duration = 2000,
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = new Vector2(300, 192),
|
||||||
|
StartTime = 5000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0));
|
||||||
|
AddUntilStep("wait for start of spinner", isSpinning);
|
||||||
|
AddUntilStep("wait for cursor to show", () => cursorAlphaAlmostEquals(1));
|
||||||
|
AddUntilStep("wait for end of spinner", () => !isSpinning());
|
||||||
|
AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestVisibleAfterComboBreak()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModNoScope
|
||||||
|
{
|
||||||
|
HiddenComboCount = { Value = 2 },
|
||||||
|
},
|
||||||
|
Autoplay = true,
|
||||||
|
PassCondition = () => true,
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject>
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = new Vector2(100, 192),
|
||||||
|
StartTime = 1000,
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = new Vector2(150, 192),
|
||||||
|
StartTime = 3000,
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = new Vector2(200, 192),
|
||||||
|
StartTime = 5000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("cursor must start visible", () => cursorAlphaAlmostEquals(1));
|
||||||
|
AddUntilStep("wait for combo", () => Player.ScoreProcessor.Combo.Value >= 2);
|
||||||
|
AddAssert("cursor must dim after combo", () => !cursorAlphaAlmostEquals(1));
|
||||||
|
AddStep("break combo", () => Player.ScoreProcessor.Combo.Set(0));
|
||||||
|
AddUntilStep("wait for cursor to show", () => cursorAlphaAlmostEquals(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isSpinning() => Player.ChildrenOfType<DrawableSpinner>().SingleOrDefault()?.Progress > 0;
|
||||||
|
|
||||||
|
private bool isBreak() => Player.IsBreakTime.Value;
|
||||||
|
|
||||||
|
private bool cursorAlphaAlmostEquals(float alpha) => Precision.AlmostEquals(Player.DrawableRuleset.Cursor.Alpha, alpha);
|
||||||
|
}
|
||||||
|
}
|
@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
|
||||||
[TestCase(6.5867229481955389d, "diffcalc-test")]
|
[TestCase(6.5295339534769958d, "diffcalc-test")]
|
||||||
[TestCase(1.0416315570967911d, "zero-length-sliders")]
|
[TestCase(1.1514260533755143d, "zero-length-sliders")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
[TestCase(8.2730989071947896d, "diffcalc-test")]
|
[TestCase(9.047752485219954d, "diffcalc-test")]
|
||||||
[TestCase(1.2726413186221039d, "zero-length-sliders")]
|
[TestCase(1.3985711787077566d, "zero-length-sliders")]
|
||||||
public void TestClockRateAdjusted(double expected, string name)
|
public void TestClockRateAdjusted(double expected, string name)
|
||||||
=> Test(expected, name, new OsuModDoubleTime());
|
=> Test(expected, name, new OsuModDoubleTime());
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private void scheduleHit() => AddStep("schedule action", () =>
|
private void scheduleHit() => AddStep("schedule action", () =>
|
||||||
{
|
{
|
||||||
var delay = hitCircle.StartTime - hitCircle.HitWindows.WindowFor(HitResult.Great) - Time.Current;
|
double delay = hitCircle.StartTime - hitCircle.HitWindows.WindowFor(HitResult.Great) - Time.Current;
|
||||||
Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(new KeyBindingPressEvent<OsuAction>(GetContainingInputManager().CurrentState, OsuAction.LeftButton)), delay);
|
Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(new KeyBindingPressEvent<OsuAction>(GetContainingInputManager().CurrentState, OsuAction.LeftButton)), delay);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
34
osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs
Normal file
34
osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneNoSpinnerStacking : TestSceneOsuPlayer
|
||||||
|
{
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo = new BeatmapInfo
|
||||||
|
{
|
||||||
|
BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 10 },
|
||||||
|
Ruleset = ruleset
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 512; i++)
|
||||||
|
{
|
||||||
|
if (i % 32 < 20)
|
||||||
|
beatmap.HitObjects.Add(new Spinner { Position = new Vector2(256, 192), StartTime = i * 200, EndTime = (i * 200) + 100 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return beatmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddAssert("player score matching expected bonus score", () =>
|
AddAssert("player score matching expected bonus score", () =>
|
||||||
{
|
{
|
||||||
// multipled by 2 to nullify the score multiplier. (autoplay mod selected)
|
// multipled by 2 to nullify the score multiplier. (autoplay mod selected)
|
||||||
var totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2;
|
double totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2;
|
||||||
return totalScore == (int)(drawableSpinner.Result.RateAdjustedRotation / 360) * new SpinnerTick().CreateJudgement().MaxNumericResult;
|
return totalScore == (int)(drawableSpinner.Result.RateAdjustedRotation / 360) * new SpinnerTick().CreateJudgement().MaxNumericResult;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||||
<PackageReference Include="Moq" Version="4.16.1" />
|
<PackageReference Include="Moq" Version="4.16.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
|
@ -39,12 +39,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
public double? Angle { get; private set; }
|
public double? Angle { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Milliseconds elapsed since the end time of the Previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
|
/// Milliseconds elapsed since the end time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double MovementTime { get; private set; }
|
public double MovementTime { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Milliseconds elapsed since from the start time of the Previous <see cref="OsuDifficultyHitObject"/> to the end time of the same Previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
|
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/> to the end time of the same previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double TravelTime { get; private set; }
|
public double TravelTime { get; private set; }
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
this.lastLastObject = (OsuHitObject)lastLastObject;
|
this.lastLastObject = (OsuHitObject)lastLastObject;
|
||||||
this.lastObject = (OsuHitObject)lastObject;
|
this.lastObject = (OsuHitObject)lastObject;
|
||||||
|
|
||||||
// Capped to 25ms to prevent difficulty calculation breaking from simulatenous objects.
|
// Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects.
|
||||||
StrainTime = Math.Max(DeltaTime, min_delta_time);
|
StrainTime = Math.Max(DeltaTime, min_delta_time);
|
||||||
|
|
||||||
setDistances(clockRate);
|
setDistances(clockRate);
|
||||||
@ -83,12 +83,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
scalingFactor *= 1 + smallCircleBonus;
|
scalingFactor *= 1 + smallCircleBonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector2 lastCursorPosition = getEndCursorPosition(lastObject);
|
||||||
|
JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
|
||||||
|
|
||||||
if (lastObject is Slider lastSlider)
|
if (lastObject is Slider lastSlider)
|
||||||
{
|
{
|
||||||
computeSliderCursorPosition(lastSlider);
|
computeSliderCursorPosition(lastSlider);
|
||||||
TravelDistance = 0;
|
TravelDistance = 0;
|
||||||
TravelTime = Math.Max(lastSlider.LazyTravelTime / clockRate, min_delta_time);
|
TravelTime = Math.Max(lastSlider.LazyTravelTime / clockRate, min_delta_time);
|
||||||
MovementTime = Math.Max(StrainTime - lastSlider.LazyTravelTime / clockRate, min_delta_time);
|
MovementTime = Math.Max(StrainTime - TravelTime, min_delta_time);
|
||||||
MovementDistance = Vector2.Subtract(lastSlider.TailCircle.StackedPosition, BaseObject.StackedPosition).Length * scalingFactor;
|
MovementDistance = Vector2.Subtract(lastSlider.TailCircle.StackedPosition, BaseObject.StackedPosition).Length * scalingFactor;
|
||||||
|
|
||||||
int repeatCount = 0;
|
int repeatCount = 0;
|
||||||
@ -147,12 +150,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
}
|
}
|
||||||
|
|
||||||
TravelDistance *= Math.Pow(1 + repeatCount / 2.5, 1.0 / 2.5); // Bonus for repeat sliders until a better per nested object strain system can be achieved.
|
TravelDistance *= Math.Pow(1 + repeatCount / 2.5, 1.0 / 2.5); // Bonus for repeat sliders until a better per nested object strain system can be achieved.
|
||||||
|
|
||||||
|
// Jump distance from the slider tail to the next object, as opposed to the lazy position of JumpDistance.
|
||||||
|
float tailJumpDistance = Vector2.Subtract(lastSlider.TailCircle.StackedPosition, BaseObject.StackedPosition).Length * scalingFactor;
|
||||||
|
|
||||||
|
// For hitobjects which continue in the direction of the slider, the player will normally follow through the slider,
|
||||||
|
// such that they're not jumping from the lazy position but rather from very close to (or the end of) the slider.
|
||||||
|
// In such cases, a leniency is applied by also considering the jump distance from the tail of the slider, and taking the minimum jump distance.
|
||||||
|
MovementDistance = Math.Min(JumpDistance, tailJumpDistance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MovementTime = StrainTime;
|
||||||
|
MovementDistance = JumpDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 lastCursorPosition = getEndCursorPosition(lastObject);
|
|
||||||
|
|
||||||
JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
|
|
||||||
MovementDistance = Math.Max(0, Math.Min(JumpDistance - 50, MovementDistance - 120)); // radius for jumpdistance is within 50 of maximum possible sliderLeniency, 120 for movement distance.
|
|
||||||
|
|
||||||
if (lastLastObject != null && !(lastLastObject is Spinner))
|
if (lastLastObject != null && !(lastLastObject is Spinner))
|
||||||
{
|
{
|
||||||
@ -202,7 +213,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
|
|
||||||
// Skip the head circle
|
// Skip the head circle
|
||||||
var scoringTimes = slider.NestedHitObjects.Skip(1).Select(t => t.StartTime);
|
var scoringTimes = slider.NestedHitObjects.Skip(1).Select(t => t.StartTime);
|
||||||
foreach (var time in scoringTimes)
|
foreach (double time in scoringTimes)
|
||||||
computeVertex(time);
|
computeVertex(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ using osu.Game.Rulesets.Difficulty.Preprocessing;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Framework.Utils;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||||
{
|
{
|
||||||
@ -37,25 +36,28 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var osuCurrObj = (OsuDifficultyHitObject)current;
|
var osuCurrObj = (OsuDifficultyHitObject)current;
|
||||||
var osuPrevObj = (OsuDifficultyHitObject)Previous[0];
|
var osuLastObj = (OsuDifficultyHitObject)Previous[0];
|
||||||
var osuLastObj = (OsuDifficultyHitObject)Previous[1];
|
var osuLastLastObj = (OsuDifficultyHitObject)Previous[1];
|
||||||
|
|
||||||
double currVelocity = (osuCurrObj.JumpDistance + osuCurrObj.TravelDistance) / osuCurrObj.StrainTime; // Start with the base distance / time
|
// Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle.
|
||||||
|
double currVelocity = osuCurrObj.JumpDistance / osuCurrObj.StrainTime;
|
||||||
|
|
||||||
if (osuPrevObj.BaseObject is Slider) // If object is a slider
|
// But if the last object is a slider, then we extend the travel velocity through the slider into the current object.
|
||||||
|
if (osuLastObj.BaseObject is Slider)
|
||||||
{
|
{
|
||||||
double movementVelocity = osuCurrObj.MovementDistance / osuCurrObj.MovementTime; // calculate the movement velocity from slider end to next note
|
double movementVelocity = osuCurrObj.MovementDistance / osuCurrObj.MovementTime; // calculate the movement velocity from slider end to current object
|
||||||
double travelVelocity = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // calculate the slider velocity from slider head to lazy end.
|
double travelVelocity = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // calculate the slider velocity from slider head to slider end.
|
||||||
|
|
||||||
currVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity.
|
currVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity.
|
||||||
}
|
}
|
||||||
|
|
||||||
double prevVelocity = (osuPrevObj.JumpDistance + osuPrevObj.TravelDistance) / osuPrevObj.StrainTime; // do the same for the previous velocity.
|
// As above, do the same for the previous hitobject.
|
||||||
|
double prevVelocity = osuLastObj.JumpDistance / osuLastObj.StrainTime;
|
||||||
|
|
||||||
if (osuLastObj.BaseObject is Slider)
|
if (osuLastLastObj.BaseObject is Slider)
|
||||||
{
|
{
|
||||||
double movementVelocity = osuPrevObj.MovementDistance / osuPrevObj.MovementTime;
|
double movementVelocity = osuLastObj.MovementDistance / osuLastObj.MovementTime;
|
||||||
double travelVelocity = osuPrevObj.TravelDistance / osuPrevObj.TravelTime;
|
double travelVelocity = osuLastObj.TravelDistance / osuLastObj.TravelTime;
|
||||||
|
|
||||||
prevVelocity = Math.Max(prevVelocity, movementVelocity + travelVelocity);
|
prevVelocity = Math.Max(prevVelocity, movementVelocity + travelVelocity);
|
||||||
}
|
}
|
||||||
@ -65,13 +67,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
|
|
||||||
double aimStrain = currVelocity; // Start strain with regular velocity.
|
double aimStrain = currVelocity; // Start strain with regular velocity.
|
||||||
|
|
||||||
if (Precision.AlmostEquals(osuCurrObj.StrainTime, osuPrevObj.StrainTime, 10)) // If rhythms are the same.
|
if (Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime) < 1.25 * Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime)) // If rhythms are the same.
|
||||||
{
|
{
|
||||||
if (osuCurrObj.Angle != null && osuPrevObj.Angle != null && osuLastObj.Angle != null)
|
if (osuCurrObj.Angle != null && osuLastObj.Angle != null && osuLastLastObj.Angle != null)
|
||||||
{
|
{
|
||||||
double currAngle = osuCurrObj.Angle.Value;
|
double currAngle = osuCurrObj.Angle.Value;
|
||||||
double prevAngle = osuPrevObj.Angle.Value;
|
|
||||||
double lastAngle = osuLastObj.Angle.Value;
|
double lastAngle = osuLastObj.Angle.Value;
|
||||||
|
double lastLastAngle = osuLastLastObj.Angle.Value;
|
||||||
|
|
||||||
// Rewarding angles, take the smaller velocity as base.
|
// Rewarding angles, take the smaller velocity as base.
|
||||||
angleBonus = Math.Min(currVelocity, prevVelocity);
|
angleBonus = Math.Min(currVelocity, prevVelocity);
|
||||||
@ -82,15 +84,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
if (osuCurrObj.StrainTime > 100) // Only buff deltaTime exceeding 300 bpm 1/2.
|
if (osuCurrObj.StrainTime > 100) // Only buff deltaTime exceeding 300 bpm 1/2.
|
||||||
acuteAngleBonus = 0;
|
acuteAngleBonus = 0;
|
||||||
else
|
else
|
||||||
acuteAngleBonus *= calcAcuteAngleBonus(prevAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
|
{
|
||||||
* Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
|
acuteAngleBonus *= calcAcuteAngleBonus(lastAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
|
||||||
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4
|
* Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
|
||||||
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Min(100, osuCurrObj.JumpDistance) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter).
|
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4
|
||||||
|
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.JumpDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter).
|
||||||
|
}
|
||||||
|
|
||||||
wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(prevAngle), 3))); // Penalize wide angles if they're repeated, reducing the penalty as the prevAngle gets more acute.
|
wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(lastAngle), 3))); // Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute.
|
||||||
acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastAngle), 3))); // Penalize acute angles if they're repeated, reducing the penalty as the lastAngle gets more obtuse.
|
acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastLastAngle), 3))); // Penalize acute angles if they're repeated, reducing the penalty as the lastLastAngle gets more obtuse.
|
||||||
|
|
||||||
angleBonus = acuteAngleBonus * acute_angle_multiplier + wideAngleBonus * wide_angle_multiplier; // add the anglebuffs together.
|
angleBonus = acuteAngleBonus * acute_angle_multiplier + wideAngleBonus * wide_angle_multiplier; // add the angle buffs together.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray();
|
Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray();
|
||||||
var oldPosition = slider.Position;
|
var oldPosition = slider.Position;
|
||||||
var oldStartTime = slider.StartTime;
|
double oldStartTime = slider.StartTime;
|
||||||
|
|
||||||
if (ControlPoint == slider.Path.ControlPoints[0])
|
if (ControlPoint == slider.Path.ControlPoints[0])
|
||||||
{
|
{
|
||||||
@ -205,7 +205,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
if (!slider.Path.HasValidLength)
|
if (!slider.Path.HasValidLength)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < slider.Path.ControlPoints.Count; i++)
|
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||||
slider.Path.ControlPoints[i].Position = oldControlPoints[i];
|
slider.Path.ControlPoints[i].Position = oldControlPoints[i];
|
||||||
|
|
||||||
slider.Position = oldPosition;
|
slider.Position = oldPosition;
|
||||||
|
@ -47,14 +47,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks
|
|||||||
if (!(hitObjects[i + 1] is OsuHitObject nextHitObject) || nextHitObject is Spinner)
|
if (!(hitObjects[i + 1] is OsuHitObject nextHitObject) || nextHitObject is Spinner)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var deltaTime = nextHitObject.StartTime - hitObject.GetEndTime();
|
double deltaTime = nextHitObject.StartTime - hitObject.GetEndTime();
|
||||||
if (deltaTime >= hitObject.TimeFadeIn + hitObject.TimePreempt)
|
if (deltaTime >= hitObject.TimeFadeIn + hitObject.TimePreempt)
|
||||||
// The objects are not visible at the same time (without mods), hence skipping.
|
// The objects are not visible at the same time (without mods), hence skipping.
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var distanceSq = (hitObject.StackedEndPosition - nextHitObject.StackedPosition).LengthSquared;
|
float distanceSq = (hitObject.StackedEndPosition - nextHitObject.StackedPosition).LengthSquared;
|
||||||
var diameter = (hitObject.Radius - overlap_leniency) * 2;
|
double diameter = (hitObject.Radius - overlap_leniency) * 2;
|
||||||
var diameterSq = diameter * diameter;
|
double diameterSq = diameter * diameter;
|
||||||
|
|
||||||
bool areOverlapping = distanceSq < diameterSq;
|
bool areOverlapping = distanceSq < diameterSq;
|
||||||
|
|
||||||
|
@ -88,21 +88,21 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks
|
|||||||
if (!(hitObjects[i + 1] is OsuHitObject nextHitObject) || nextHitObject is Spinner)
|
if (!(hitObjects[i + 1] is OsuHitObject nextHitObject) || nextHitObject is Spinner)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var deltaTime = nextHitObject.StartTime - hitObject.GetEndTime();
|
double deltaTime = nextHitObject.StartTime - hitObject.GetEndTime();
|
||||||
|
|
||||||
// Ignore objects that are far enough apart in time to not be considered the same pattern.
|
// Ignore objects that are far enough apart in time to not be considered the same pattern.
|
||||||
if (deltaTime > pattern_lifetime)
|
if (deltaTime > pattern_lifetime)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Relying on FastInvSqrt is probably good enough here. We'll be taking the difference between distances later, hence square not being sufficient.
|
// Relying on FastInvSqrt is probably good enough here. We'll be taking the difference between distances later, hence square not being sufficient.
|
||||||
var distance = (hitObject.StackedEndPosition - nextHitObject.StackedPosition).LengthFast;
|
float distance = (hitObject.StackedEndPosition - nextHitObject.StackedPosition).LengthFast;
|
||||||
|
|
||||||
// Ignore stacks and half-stacks, as these are close enough to where they can't be confused for being time-distanced.
|
// Ignore stacks and half-stacks, as these are close enough to where they can't be confused for being time-distanced.
|
||||||
if (distance < stack_leniency)
|
if (distance < stack_leniency)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var observedTimeDistance = new ObservedTimeDistance(nextHitObject.StartTime, deltaTime, distance);
|
var observedTimeDistance = new ObservedTimeDistance(nextHitObject.StartTime, deltaTime, distance);
|
||||||
var expectedDistance = getExpectedDistance(prevObservedTimeDistances, observedTimeDistance);
|
double expectedDistance = getExpectedDistance(prevObservedTimeDistances, observedTimeDistance);
|
||||||
|
|
||||||
if (expectedDistance == 0)
|
if (expectedDistance == 0)
|
||||||
{
|
{
|
||||||
@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks
|
|||||||
|
|
||||||
private double getExpectedDistance(IEnumerable<ObservedTimeDistance> prevObservedTimeDistances, ObservedTimeDistance observedTimeDistance)
|
private double getExpectedDistance(IEnumerable<ObservedTimeDistance> prevObservedTimeDistances, ObservedTimeDistance observedTimeDistance)
|
||||||
{
|
{
|
||||||
var observations = prevObservedTimeDistances.Count();
|
int observations = prevObservedTimeDistances.Count();
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
var gridSizeIndex = Array.IndexOf(grid_sizes, editorBeatmap.BeatmapInfo.GridSize);
|
int gridSizeIndex = Array.IndexOf(grid_sizes, editorBeatmap.BeatmapInfo.GridSize);
|
||||||
if (gridSizeIndex >= 0)
|
if (gridSizeIndex >= 0)
|
||||||
currentGridSizeIndex = gridSizeIndex;
|
currentGridSizeIndex = gridSizeIndex;
|
||||||
updateSpacing();
|
updateSpacing();
|
||||||
|
@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
var firstObj = beatmap.HitObjects[0];
|
var firstObj = beatmap.HitObjects[0];
|
||||||
var startDelay = firstObj.StartTime - firstObj.TimePreempt;
|
double startDelay = firstObj.StartTime - firstObj.TimePreempt;
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(startDelay + break_close_late))
|
using (BeginAbsoluteSequence(startDelay + break_close_late))
|
||||||
leaveBreak();
|
leaveBreak();
|
||||||
|
@ -5,6 +5,8 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -17,6 +19,9 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModHidden : ModHidden, IHidesApproachCircles
|
public class OsuModHidden : ModHidden, IHidesApproachCircles
|
||||||
{
|
{
|
||||||
|
[SettingSource("Only fade approach circles", "The main object body will not fade when enabled.")]
|
||||||
|
public Bindable<bool> OnlyFadeApproachCircles { get; } = new BindableBool();
|
||||||
|
|
||||||
public override string Description => @"Play with no approach circles and fading circles/sliders.";
|
public override string Description => @"Play with no approach circles and fading circles/sliders.";
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
|
|
||||||
@ -44,15 +49,15 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
applyState(hitObject, true);
|
applyHiddenState(hitObject, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
applyState(hitObject, false);
|
applyHiddenState(hitObject, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyState(DrawableHitObject drawableObject, bool increaseVisibility)
|
private void applyHiddenState(DrawableHitObject drawableObject, bool increaseVisibility)
|
||||||
{
|
{
|
||||||
if (!(drawableObject is DrawableOsuHitObject drawableOsuObject))
|
if (!(drawableObject is DrawableOsuHitObject drawableOsuObject))
|
||||||
return;
|
return;
|
||||||
@ -61,6 +66,24 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
(double fadeStartTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject);
|
(double fadeStartTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject);
|
||||||
|
|
||||||
|
// process approach circle hiding first (to allow for early return below).
|
||||||
|
if (!increaseVisibility)
|
||||||
|
{
|
||||||
|
if (drawableObject is DrawableHitCircle circle)
|
||||||
|
{
|
||||||
|
using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt))
|
||||||
|
circle.ApproachCircle.Hide();
|
||||||
|
}
|
||||||
|
else if (drawableObject is DrawableSpinner spinner)
|
||||||
|
{
|
||||||
|
spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner);
|
||||||
|
hideSpinnerApproachCircle(spinner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OnlyFadeApproachCircles.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (drawableObject)
|
switch (drawableObject)
|
||||||
{
|
{
|
||||||
case DrawableSliderTail _:
|
case DrawableSliderTail _:
|
||||||
@ -84,12 +107,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
// only fade the circle piece (not the approach circle) for the increased visibility object.
|
// only fade the circle piece (not the approach circle) for the increased visibility object.
|
||||||
fadeTarget = circle.CirclePiece;
|
fadeTarget = circle.CirclePiece;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// we don't want to see the approach circle
|
|
||||||
using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt))
|
|
||||||
circle.ApproachCircle.Hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
using (drawableObject.BeginAbsoluteSequence(fadeStartTime))
|
using (drawableObject.BeginAbsoluteSequence(fadeStartTime))
|
||||||
fadeTarget.FadeOut(fadeDuration);
|
fadeTarget.FadeOut(fadeDuration);
|
||||||
@ -111,9 +128,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
// hide elements we don't care about.
|
// hide elements we don't care about.
|
||||||
// todo: hide background
|
// todo: hide background
|
||||||
|
|
||||||
spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner);
|
|
||||||
hideSpinnerApproachCircle(spinner);
|
|
||||||
|
|
||||||
using (spinner.BeginAbsoluteSequence(fadeStartTime))
|
using (spinner.BeginAbsoluteSequence(fadeStartTime))
|
||||||
spinner.FadeOut(fadeDuration);
|
spinner.FadeOut(fadeDuration);
|
||||||
|
|
||||||
@ -141,11 +155,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
static (double fadeStartTime, double fadeDuration) getParameters(OsuHitObject hitObject)
|
static (double fadeStartTime, double fadeDuration) getParameters(OsuHitObject hitObject)
|
||||||
{
|
{
|
||||||
var fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn;
|
double fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn;
|
||||||
var fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier;
|
double fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier;
|
||||||
|
|
||||||
// new duration from completed fade in to end (before fading out)
|
// new duration from completed fade in to end (before fading out)
|
||||||
var longFadeDuration = hitObject.GetEndTime() - fadeOutStartTime;
|
double longFadeDuration = hitObject.GetEndTime() - fadeOutStartTime;
|
||||||
|
|
||||||
switch (hitObject)
|
switch (hitObject)
|
||||||
{
|
{
|
||||||
@ -153,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
return (fadeOutStartTime, longFadeDuration);
|
return (fadeOutStartTime, longFadeDuration);
|
||||||
|
|
||||||
case SliderTick _:
|
case SliderTick _:
|
||||||
var tickFadeOutDuration = Math.Min(hitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000);
|
double tickFadeOutDuration = Math.Min(hitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000);
|
||||||
return (hitObject.StartTime - tickFadeOutDuration, tickFadeOutDuration);
|
return (hitObject.StartTime - tickFadeOutDuration, tickFadeOutDuration);
|
||||||
|
|
||||||
case Spinner _:
|
case Spinner _:
|
||||||
|
@ -2,22 +2,27 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.UI;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModNoScope : Mod, IUpdatableByPlayfield, IApplicableToScoreProcessor
|
public class OsuModNoScope : Mod, IUpdatableByPlayfield, IApplicableToScoreProcessor, IApplicableToPlayer, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Slightly higher than the cutoff for <see cref="Drawable.IsPresent"/>.
|
/// Slightly higher than the cutoff for <see cref="Drawable.IsPresent"/>.
|
||||||
@ -34,8 +39,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
private BindableNumber<int> currentCombo;
|
private BindableNumber<int> currentCombo;
|
||||||
|
private IBindable<bool> isBreakTime;
|
||||||
|
private PeriodTracker spinnerPeriods;
|
||||||
|
|
||||||
private float targetAlpha;
|
private float comboBasedAlpha;
|
||||||
|
|
||||||
[SettingSource(
|
[SettingSource(
|
||||||
"Hidden at combo",
|
"Hidden at combo",
|
||||||
@ -52,6 +59,16 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
||||||
|
|
||||||
|
public void ApplyToPlayer(Player player)
|
||||||
|
{
|
||||||
|
isBreakTime = player.IsBreakTime.GetBoundCopy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
spinnerPeriods = new PeriodTracker(beatmap.HitObjects.OfType<Spinner>().Select(b => new Period(b.StartTime - transition_duration, b.EndTime)));
|
||||||
|
}
|
||||||
|
|
||||||
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
|
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
|
||||||
{
|
{
|
||||||
if (HiddenComboCount.Value == 0) return;
|
if (HiddenComboCount.Value == 0) return;
|
||||||
@ -59,12 +76,14 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
currentCombo = scoreProcessor.Combo.GetBoundCopy();
|
currentCombo = scoreProcessor.Combo.GetBoundCopy();
|
||||||
currentCombo.BindValueChanged(combo =>
|
currentCombo.BindValueChanged(combo =>
|
||||||
{
|
{
|
||||||
targetAlpha = Math.Max(min_alpha, 1 - (float)combo.NewValue / HiddenComboCount.Value);
|
comboBasedAlpha = Math.Max(min_alpha, 1 - (float)combo.NewValue / HiddenComboCount.Value);
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Update(Playfield playfield)
|
public virtual void Update(Playfield playfield)
|
||||||
{
|
{
|
||||||
|
bool shouldAlwaysShowCursor = isBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime);
|
||||||
|
float targetAlpha = shouldAlwaysShowCursor ? 1 : comboBasedAlpha;
|
||||||
playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / transition_duration, 0, 1));
|
playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / transition_duration, 0, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
// to allow jumps and prevent too sharp turns during streams.
|
// to allow jumps and prevent too sharp turns during streams.
|
||||||
|
|
||||||
// Allow maximum jump angle when jump distance is more than half of playfield diagonal length
|
// Allow maximum jump angle when jump distance is more than half of playfield diagonal length
|
||||||
var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * Math.Min(1f, distanceToPrev / (playfield_diagonal * 0.5f));
|
double randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * Math.Min(1f, distanceToPrev / (playfield_diagonal * 0.5f));
|
||||||
|
|
||||||
current.AngleRad = (float)randomAngleRad + previous.AngleRad;
|
current.AngleRad = (float)randomAngleRad + previous.AngleRad;
|
||||||
if (current.AngleRad < 0)
|
if (current.AngleRad < 0)
|
||||||
@ -171,11 +171,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
// Clamp slider position to the placement area
|
// Clamp slider position to the placement area
|
||||||
// If the slider is larger than the playfield, force it to stay at the original position
|
// If the slider is larger than the playfield, force it to stay at the original position
|
||||||
var newX = possibleMovementBounds.Width < 0
|
float newX = possibleMovementBounds.Width < 0
|
||||||
? objectInfo.PositionOriginal.X
|
? objectInfo.PositionOriginal.X
|
||||||
: Math.Clamp(previousPosition.X, possibleMovementBounds.Left, possibleMovementBounds.Right);
|
: Math.Clamp(previousPosition.X, possibleMovementBounds.Left, possibleMovementBounds.Right);
|
||||||
|
|
||||||
var newY = possibleMovementBounds.Height < 0
|
float newY = possibleMovementBounds.Height < 0
|
||||||
? objectInfo.PositionOriginal.Y
|
? objectInfo.PositionOriginal.Y
|
||||||
: Math.Clamp(previousPosition.Y, possibleMovementBounds.Top, possibleMovementBounds.Bottom);
|
: Math.Clamp(previousPosition.Y, possibleMovementBounds.Top, possibleMovementBounds.Bottom);
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Take the circle radius into account.
|
// Take the circle radius into account.
|
||||||
var radius = (float)slider.Radius;
|
float radius = (float)slider.Radius;
|
||||||
|
|
||||||
minX -= radius;
|
minX -= radius;
|
||||||
minY -= radius;
|
minY -= radius;
|
||||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
// because the spinner is under the gameplay clock, it is affected by rate adjustments on the track;
|
// because the spinner is under the gameplay clock, it is affected by rate adjustments on the track;
|
||||||
// for that reason using ElapsedFrameTime directly leads to fewer SPM with Half Time and more SPM with Double Time.
|
// for that reason using ElapsedFrameTime directly leads to fewer SPM with Half Time and more SPM with Double Time.
|
||||||
// for spinners we want the real (wall clock) elapsed time; to achieve that, unapply the clock rate locally here.
|
// for spinners we want the real (wall clock) elapsed time; to achieve that, unapply the clock rate locally here.
|
||||||
var rateIndependentElapsedTime = spinner.Clock.ElapsedFrameTime / spinner.Clock.Rate;
|
double rateIndependentElapsedTime = spinner.Clock.ElapsedFrameTime / spinner.Clock.Rate;
|
||||||
spinner.RotationTracker.AddRotation(MathUtils.RadiansToDegrees((float)rateIndependentElapsedTime * 0.03f));
|
spinner.RotationTracker.AddRotation(MathUtils.RadiansToDegrees((float)rateIndependentElapsedTime * 0.03f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,8 +193,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
private IEnumerable<double> generateBeats(IBeatmap beatmap)
|
private IEnumerable<double> generateBeats(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
var startTime = originalHitObjects.First().StartTime;
|
double startTime = originalHitObjects.First().StartTime;
|
||||||
var endTime = originalHitObjects.Last().GetEndTime();
|
double endTime = originalHitObjects.Last().GetEndTime();
|
||||||
|
|
||||||
var beats = beatmap.ControlPointInfo.TimingPoints
|
var beats = beatmap.ControlPointInfo.TimingPoints
|
||||||
// Ignore timing points after endTime
|
// Ignore timing points after endTime
|
||||||
@ -208,9 +208,9 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// Remove beats that are too close to the next one (e.g. due to timing point changes)
|
// Remove beats that are too close to the next one (e.g. due to timing point changes)
|
||||||
for (var i = beats.Count - 2; i >= 0; i--)
|
for (int i = beats.Count - 2; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var beat = beats[i];
|
double beat = beats[i];
|
||||||
|
|
||||||
if (!definitelyBigger(beats[i + 1] - beat, beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2))
|
if (!definitelyBigger(beats[i + 1] - beat, beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2))
|
||||||
beats.RemoveAt(i);
|
beats.RemoveAt(i);
|
||||||
@ -250,13 +250,13 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
// Other kinds of combo info are also added in the process
|
// Other kinds of combo info are also added in the process
|
||||||
var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList();
|
var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList();
|
||||||
|
|
||||||
for (var i = 0; i < combos.Count; i++)
|
for (int i = 0; i < combos.Count; i++)
|
||||||
{
|
{
|
||||||
var group = combos[i].ToList();
|
var group = combos[i].ToList();
|
||||||
group.First().NewCombo = true;
|
group.First().NewCombo = true;
|
||||||
group.Last().LastInCombo = true;
|
group.Last().LastInCombo = true;
|
||||||
|
|
||||||
for (var j = 0; j < group.Count; j++)
|
for (int j = 0; j < group.Count; j++)
|
||||||
{
|
{
|
||||||
var x = group[j];
|
var x = group[j];
|
||||||
x.ComboIndex = i;
|
x.ComboIndex = i;
|
||||||
@ -273,17 +273,17 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
const float two_pi = MathF.PI * 2;
|
const float two_pi = MathF.PI * 2;
|
||||||
|
|
||||||
var direction = two_pi * nextSingle();
|
float direction = two_pi * nextSingle();
|
||||||
var maxComboIndex = hitObjects.Last().ComboIndex;
|
int maxComboIndex = hitObjects.Last().ComboIndex;
|
||||||
|
|
||||||
for (var i = 0; i < hitObjects.Count; i++)
|
for (int i = 0; i < hitObjects.Count; i++)
|
||||||
{
|
{
|
||||||
var obj = hitObjects[i];
|
var obj = hitObjects[i];
|
||||||
var lastPos = i == 0
|
var lastPos = i == 0
|
||||||
? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2)
|
? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2)
|
||||||
: hitObjects[i - 1].Position;
|
: hitObjects[i - 1].Position;
|
||||||
|
|
||||||
var distance = maxComboIndex == 0
|
float distance = maxComboIndex == 0
|
||||||
? (float)obj.Radius
|
? (float)obj.Radius
|
||||||
: mapRange(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance);
|
: mapRange(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance);
|
||||||
if (obj.NewCombo) distance *= 1.5f;
|
if (obj.NewCombo) distance *= 1.5f;
|
||||||
@ -292,7 +292,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
// Attempt to place the circle at a place that does not overlap with previous ones
|
// Attempt to place the circle at a place that does not overlap with previous ones
|
||||||
|
|
||||||
var tryCount = 0;
|
int tryCount = 0;
|
||||||
|
|
||||||
// for checking overlap
|
// for checking overlap
|
||||||
var precedingObjects = hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count).ToList();
|
var precedingObjects = hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count).ToList();
|
||||||
@ -363,7 +363,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
var beats = new List<double>();
|
var beats = new List<double>();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
var currentTime = timingPoint.Time;
|
double currentTime = timingPoint.Time;
|
||||||
|
|
||||||
while (!definitelyBigger(currentTime, mapEndTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint)
|
while (!definitelyBigger(currentTime, mapEndTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint)
|
||||||
{
|
{
|
||||||
@ -377,7 +377,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
private OsuHitObject getClosestHitObject(List<OsuHitObject> hitObjects, double time)
|
private OsuHitObject getClosestHitObject(List<OsuHitObject> hitObjects, double time)
|
||||||
{
|
{
|
||||||
var precedingIndex = hitObjects.FindLastIndex(h => h.StartTime < time);
|
int precedingIndex = hitObjects.FindLastIndex(h => h.StartTime < time);
|
||||||
|
|
||||||
if (precedingIndex == hitObjects.Count - 1) return hitObjects[precedingIndex];
|
if (precedingIndex == hitObjects.Count - 1) return hitObjects[precedingIndex];
|
||||||
|
|
||||||
@ -457,7 +457,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
private void clampToPlayfield(OsuHitObject obj)
|
private void clampToPlayfield(OsuHitObject obj)
|
||||||
{
|
{
|
||||||
var position = obj.Position;
|
var position = obj.Position;
|
||||||
var radius = (float)obj.Radius;
|
float radius = (float)obj.Radius;
|
||||||
|
|
||||||
if (position.Y < radius)
|
if (position.Y < radius)
|
||||||
position.Y = radius;
|
position.Y = radius;
|
||||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector;
|
Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector;
|
||||||
Vector2 pointEndPosition = startPosition + fraction * distanceVector;
|
Vector2 pointEndPosition = startPosition + fraction * distanceVector;
|
||||||
|
|
||||||
GetFadeTimes(start, end, (float)d / distance, out var fadeInTime, out var fadeOutTime);
|
GetFadeTimes(start, end, (float)d / distance, out double fadeInTime, out double fadeOutTime);
|
||||||
|
|
||||||
FollowPoint fp;
|
FollowPoint fp;
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
|
|
||||||
// The lifetime start will match the fade-in time of the first follow point.
|
// The lifetime start will match the fade-in time of the first follow point.
|
||||||
float fraction = (int)(FollowPointConnection.SPACING * 1.5) / distanceVector.Length;
|
float fraction = (int)(FollowPointConnection.SPACING * 1.5) / distanceVector.Length;
|
||||||
FollowPointConnection.GetFadeTimes(Start, End, fraction, out var fadeInTime, out _);
|
FollowPointConnection.GetFadeTimes(Start, End, fraction, out double fadeInTime, out _);
|
||||||
|
|
||||||
LifetimeStart = fadeInTime;
|
LifetimeStart = fadeInTime;
|
||||||
LifetimeEnd = double.MaxValue; // This will be set by the connection.
|
LifetimeEnd = double.MaxValue; // This will be set by the connection.
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
{
|
{
|
||||||
var newEntry = new FollowPointLifetimeEntry(hitObject);
|
var newEntry = new FollowPointLifetimeEntry(hitObject);
|
||||||
|
|
||||||
var index = lifetimeEntries.AddInPlace(newEntry, Comparer<FollowPointLifetimeEntry>.Create((e1, e2) =>
|
int index = lifetimeEntries.AddInPlace(newEntry, Comparer<FollowPointLifetimeEntry>.Create((e1, e2) =>
|
||||||
{
|
{
|
||||||
int comp = e1.Start.StartTime.CompareTo(e2.Start.StartTime);
|
int comp = e1.Start.StartTime.CompareTo(e2.Start.StartTime);
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
set => StackHeightBindable.Value = value;
|
set => StackHeightBindable.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);
|
public virtual Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);
|
||||||
|
|
||||||
public double Radius => OBJECT_RADIUS * Scale;
|
public double Radius => OBJECT_RADIUS * Scale;
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal double LazyTravelTime;
|
internal double LazyTravelTime;
|
||||||
|
|
||||||
public List<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
|
public IList<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IList<HitSampleInfo> TailSamples { get; private set; }
|
public IList<HitSampleInfo> TailSamples { get; private set; }
|
||||||
@ -157,8 +157,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
base.CreateNestedHitObjects(cancellationToken);
|
base.CreateNestedHitObjects(cancellationToken);
|
||||||
|
|
||||||
foreach (var e in
|
var sliderEvents = SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken);
|
||||||
SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken))
|
|
||||||
|
foreach (var e in sliderEvents)
|
||||||
{
|
{
|
||||||
switch (e.Type)
|
switch (e.Type)
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
{
|
{
|
||||||
@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaximumBonusSpins { get; protected set; } = 1;
|
public int MaximumBonusSpins { get; protected set; } = 1;
|
||||||
|
|
||||||
|
public override Vector2 StackOffset => Vector2.Zero;
|
||||||
|
|
||||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
@ -70,8 +70,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
Slider slider = drawableSlider.HitObject;
|
Slider slider = drawableSlider.HitObject;
|
||||||
|
|
||||||
var span = slider.SpanAt(completionProgress);
|
int span = slider.SpanAt(completionProgress);
|
||||||
var spanProgress = slider.ProgressAt(completionProgress);
|
double spanProgress = slider.ProgressAt(completionProgress);
|
||||||
|
|
||||||
double start = 0;
|
double start = 0;
|
||||||
double end = SnakingIn.Value ? Math.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1;
|
double end = SnakingIn.Value ? Math.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1;
|
||||||
@ -110,8 +110,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
|
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
|
||||||
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
|
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
|
||||||
|
|
||||||
var lastSnakedStart = SnakedStart ?? 0;
|
double lastSnakedStart = SnakedStart ?? 0;
|
||||||
var lastSnakedEnd = SnakedEnd ?? 0;
|
double lastSnakedEnd = SnakedEnd ?? 0;
|
||||||
|
|
||||||
SnakedStart = null;
|
SnakedStart = null;
|
||||||
SnakedEnd = null;
|
SnakedEnd = null;
|
||||||
|
@ -62,9 +62,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
var thisAngle = -MathUtils.RadiansToDegrees(MathF.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
|
float thisAngle = -MathUtils.RadiansToDegrees(MathF.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
|
||||||
|
|
||||||
var delta = thisAngle - lastAngle;
|
float delta = thisAngle - lastAngle;
|
||||||
|
|
||||||
if (Tracking)
|
if (Tracking)
|
||||||
AddRotation(delta);
|
AddRotation(delta);
|
||||||
|
@ -14,7 +14,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using static osu.Game.Skinning.LegacySkinConfiguration;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
{
|
{
|
||||||
@ -158,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
if (hasNumber)
|
if (hasNumber)
|
||||||
{
|
{
|
||||||
var legacyVersion = skin.GetConfig<LegacySetting, decimal>(LegacySetting.Version)?.Value;
|
decimal? legacyVersion = skin.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value;
|
||||||
|
|
||||||
if (legacyVersion >= 2.0m)
|
if (legacyVersion >= 2.0m)
|
||||||
// legacy skins of version 2.0 and newer only apply very short fade out to the number piece.
|
// legacy skins of version 2.0 and newer only apply very short fade out to the number piece.
|
||||||
|
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
// careful: need to call this exactly once for all calculations in a frame
|
// careful: need to call this exactly once for all calculations in a frame
|
||||||
// as the function has a random factor in it
|
// as the function has a random factor in it
|
||||||
var metreHeight = getMetreHeight(DrawableSpinner.Progress);
|
float metreHeight = getMetreHeight(DrawableSpinner.Progress);
|
||||||
|
|
||||||
// hack to make the metre blink up from below than down from above.
|
// hack to make the metre blink up from below than down from above.
|
||||||
// move down the container to be able to apply masking for the metre,
|
// move down the container to be able to apply masking for the metre,
|
||||||
|
@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.Statistics
|
|||||||
// Likewise sin(pi/2)=1 and sin(3pi/2)=-1, whereas we actually want these values to appear on the bottom/top respectively, so the y-coordinate also needs to be inverted.
|
// Likewise sin(pi/2)=1 and sin(3pi/2)=-1, whereas we actually want these values to appear on the bottom/top respectively, so the y-coordinate also needs to be inverted.
|
||||||
//
|
//
|
||||||
// We also need to apply the anti-clockwise rotation.
|
// We also need to apply the anti-clockwise rotation.
|
||||||
var rotatedAngle = finalAngle - MathUtils.DegreesToRadians(rotation);
|
double rotatedAngle = finalAngle - MathUtils.DegreesToRadians(rotation);
|
||||||
var rotatedCoordinate = -1 * new Vector2((float)Math.Cos(rotatedAngle), (float)Math.Sin(rotatedAngle));
|
var rotatedCoordinate = -1 * new Vector2((float)Math.Cos(rotatedAngle), (float)Math.Sin(rotatedAngle));
|
||||||
|
|
||||||
Vector2 localCentre = new Vector2(points_per_dimension - 1) / 2;
|
Vector2 localCentre = new Vector2(points_per_dimension - 1) / 2;
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
/// <returns>The new position of the hit object, relative to the previous one.</returns>
|
/// <returns>The new position of the hit object, relative to the previous one.</returns>
|
||||||
public static Vector2 RotateAwayFromEdge(Vector2 prevObjectPos, Vector2 posRelativeToPrev, float rotationRatio = 0.5f)
|
public static Vector2 RotateAwayFromEdge(Vector2 prevObjectPos, Vector2 posRelativeToPrev, float rotationRatio = 0.5f)
|
||||||
{
|
{
|
||||||
var relativeRotationDistance = 0f;
|
float relativeRotationDistance = 0f;
|
||||||
|
|
||||||
if (prevObjectPos.X < playfield_middle.X)
|
if (prevObjectPos.X < playfield_middle.X)
|
||||||
{
|
{
|
||||||
@ -88,16 +88,16 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
/// <returns>The rotated vector.</returns>
|
/// <returns>The rotated vector.</returns>
|
||||||
public static Vector2 RotateVectorTowardsVector(Vector2 initial, Vector2 destination, float rotationRatio)
|
public static Vector2 RotateVectorTowardsVector(Vector2 initial, Vector2 destination, float rotationRatio)
|
||||||
{
|
{
|
||||||
var initialAngleRad = MathF.Atan2(initial.Y, initial.X);
|
float initialAngleRad = MathF.Atan2(initial.Y, initial.X);
|
||||||
var destAngleRad = MathF.Atan2(destination.Y, destination.X);
|
float destAngleRad = MathF.Atan2(destination.Y, destination.X);
|
||||||
|
|
||||||
var diff = destAngleRad - initialAngleRad;
|
float diff = destAngleRad - initialAngleRad;
|
||||||
|
|
||||||
while (diff < -MathF.PI) diff += 2 * MathF.PI;
|
while (diff < -MathF.PI) diff += 2 * MathF.PI;
|
||||||
|
|
||||||
while (diff > MathF.PI) diff -= 2 * MathF.PI;
|
while (diff > MathF.PI) diff -= 2 * MathF.PI;
|
||||||
|
|
||||||
var finalAngleRad = initialAngleRad + rotationRatio * diff;
|
float finalAngleRad = initialAngleRad + rotationRatio * diff;
|
||||||
|
|
||||||
return new Vector2(
|
return new Vector2(
|
||||||
initial.Length * MathF.Cos(finalAngleRad),
|
initial.Length * MathF.Cos(finalAngleRad),
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
|
|
||||||
var expectedSampleNames = new[]
|
string[] expectedSampleNames =
|
||||||
{
|
{
|
||||||
string.Empty,
|
string.Empty,
|
||||||
string.Empty,
|
string.Empty,
|
||||||
@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
HitSampleInfo.HIT_WHISTLE,
|
HitSampleInfo.HIT_WHISTLE,
|
||||||
HitSampleInfo.HIT_WHISTLE,
|
HitSampleInfo.HIT_WHISTLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
var actualSampleNames = new List<string>();
|
var actualSampleNames = new List<string>();
|
||||||
|
|
||||||
// due to pooling we can't access all samples right away due to object re-use,
|
// due to pooling we can't access all samples right away due to object re-use,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
{
|
{
|
||||||
case IHasDistance distanceData:
|
case IHasDistance distanceData:
|
||||||
{
|
{
|
||||||
if (shouldConvertSliderToHits(obj, beatmap, distanceData, out var taikoDuration, out var tickSpacing))
|
if (shouldConvertSliderToHits(obj, beatmap, distanceData, out int taikoDuration, out double tickSpacing))
|
||||||
{
|
{
|
||||||
List<IList<HitSampleInfo>> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
|
IList<IList<HitSampleInfo>> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
var corrected = samples.ToList();
|
var corrected = samples.ToList();
|
||||||
|
|
||||||
for (var i = 0; i < corrected.Count; i++)
|
for (int i = 0; i < corrected.Count; i++)
|
||||||
{
|
{
|
||||||
var s = corrected[i];
|
var s = corrected[i];
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
validActionPressed = HitActions.Contains(e.Action);
|
validActionPressed = HitActions.Contains(e.Action);
|
||||||
|
|
||||||
// Only count this as handled if the new judgement is a hit
|
// Only count this as handled if the new judgement is a hit
|
||||||
var result = UpdateResult(true);
|
bool result = UpdateResult(true);
|
||||||
if (IsHit)
|
if (IsHit)
|
||||||
HitAction = e.Action;
|
HitAction = e.Action;
|
||||||
|
|
||||||
|
@ -185,9 +185,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
nextTick?.TriggerResult(true);
|
nextTick?.TriggerResult(true);
|
||||||
|
|
||||||
var numHits = ticks.Count(r => r.IsHit);
|
int numHits = ticks.Count(r => r.IsHit);
|
||||||
|
|
||||||
var completion = (float)numHits / HitObject.RequiredHits;
|
float completion = (float)numHits / HitObject.RequiredHits;
|
||||||
|
|
||||||
expandingRing
|
expandingRing
|
||||||
.FadeTo(expandingRing.Alpha + Math.Clamp(completion / 16, 0.1f, 0.6f), 50)
|
.FadeTo(expandingRing.Alpha + Math.Clamp(completion / 16, 0.1f, 0.6f), 50)
|
||||||
@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
if (Time.Current < HitObject.StartTime)
|
if (Time.Current < HitObject.StartTime)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre;
|
bool isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre;
|
||||||
|
|
||||||
// Ensure alternating centre and rim hits
|
// Ensure alternating centre and rim hits
|
||||||
if (lastWasCentre == isCentre)
|
if (lastWasCentre == isCentre)
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
// because the right half is flipped, we need to position using width - position to get the true "topleft" origin position
|
// because the right half is flipped, we need to position using width - position to get the true "topleft" origin position
|
||||||
float negativeScaleAdjust = content.Width / ratio;
|
float negativeScaleAdjust = content.Width / ratio;
|
||||||
|
|
||||||
if (skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.1m)
|
if (skin.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value >= 2.1m)
|
||||||
{
|
{
|
||||||
left.Centre.Position = new Vector2(0, taiko_bar_y) * ratio;
|
left.Centre.Position = new Vector2(0, taiko_bar_y) * ratio;
|
||||||
right.Centre.Position = new Vector2(negativeScaleAdjust - 56, taiko_bar_y) * ratio;
|
right.Centre.Position = new Vector2(negativeScaleAdjust - 56, taiko_bar_y) * ratio;
|
||||||
|
@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
|
|
||||||
case TaikoSkinComponents.TaikoExplosionOk:
|
case TaikoSkinComponents.TaikoExplosionOk:
|
||||||
case TaikoSkinComponents.TaikoExplosionGreat:
|
case TaikoSkinComponents.TaikoExplosionGreat:
|
||||||
var hitName = getHitName(taikoComponent.Component);
|
string hitName = getHitName(taikoComponent.Component);
|
||||||
var hitSprite = this.GetAnimation(hitName, true, false);
|
var hitSprite = this.GetAnimation(hitName, true, false);
|
||||||
|
|
||||||
if (hitSprite != null)
|
if (hitSprite != null)
|
||||||
@ -162,10 +162,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
foreach (var name in base.LookupNames)
|
foreach (string name in base.LookupNames)
|
||||||
yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-");
|
yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-");
|
||||||
|
|
||||||
foreach (var name in base.LookupNames)
|
foreach (string name in base.LookupNames)
|
||||||
yield return name;
|
yield return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
// Remove any auxiliary hit notes that were spawned during a drum roll but subsequently rewound.
|
// Remove any auxiliary hit notes that were spawned during a drum roll but subsequently rewound.
|
||||||
for (var i = AliveInternalChildren.Count - 1; i >= 0; i--)
|
for (int i = AliveInternalChildren.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var flyingHit = (DrawableFlyingHit)AliveInternalChildren[i];
|
var flyingHit = (DrawableFlyingHit)AliveInternalChildren[i];
|
||||||
if (Time.Current <= flyingHit.HitObject.StartTime)
|
if (Time.Current <= flyingHit.HitObject.StartTime)
|
||||||
|
@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
if (skin == null) return;
|
if (skin == null) return;
|
||||||
|
|
||||||
foreach (var frameIndex in clear_animation_sequence)
|
foreach (int frameIndex in clear_animation_sequence)
|
||||||
{
|
{
|
||||||
var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex);
|
var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex);
|
||||||
|
|
||||||
|
@ -775,5 +775,22 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.That(seventh.ControlPoints[4].Type == null);
|
Assert.That(seventh.ControlPoints[4].Type == null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSliderLengthExtensionEdgeCase()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("duplicate-last-position-slider.osu"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var decoded = decoder.Decode(stream);
|
||||||
|
|
||||||
|
var path = ((IHasPath)decoded.HitObjects[0]).Path;
|
||||||
|
|
||||||
|
Assert.That(path.ExpectedDistance.Value, Is.EqualTo(2));
|
||||||
|
Assert.That(path.Distance, Is.EqualTo(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
|
|
||||||
protected override bool ShouldSkipLine(string line)
|
protected override bool ShouldSkipLine(string line)
|
||||||
{
|
{
|
||||||
var result = base.ShouldSkipLine(line);
|
bool result = base.ShouldSkipLine(line);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
ParsedLines.Add(line);
|
ParsedLines.Add(line);
|
||||||
|
@ -82,7 +82,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
|
|
||||||
var tempPath = TestResources.GetTestBeatmapForImport();
|
string tempPath = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -196,7 +196,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -251,7 +251,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -302,7 +302,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -424,7 +424,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
checkBeatmapCount(osu, 12);
|
checkBeatmapCount(osu, 12);
|
||||||
checkSingleReferencedFileCount(osu, 18);
|
checkSingleReferencedFileCount(osu, 18);
|
||||||
|
|
||||||
var brokenTempFilename = TestResources.GetTestBeatmapForImport();
|
string brokenTempFilename = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
MemoryStream brokenOsu = new MemoryStream();
|
MemoryStream brokenOsu = new MemoryStream();
|
||||||
MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename));
|
MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename));
|
||||||
@ -594,7 +594,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
|
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
var importer = new ArchiveImportIPCChannel(client);
|
var importer = new ArchiveImportIPCChannel(client);
|
||||||
if (!importer.ImportAsync(temp).Wait(10000))
|
if (!importer.ImportAsync(temp).Wait(10000))
|
||||||
@ -619,7 +619,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
using (File.OpenRead(temp))
|
using (File.OpenRead(temp))
|
||||||
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
||||||
ensureLoaded(osu);
|
ensureLoaded(osu);
|
||||||
@ -642,7 +642,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -684,7 +684,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
string subfolder = Path.Combine(extractedFolder, "subfolder");
|
string subfolder = Path.Combine(extractedFolder, "subfolder");
|
||||||
@ -729,7 +729,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
string dataFolder = Path.Combine(extractedFolder, "actual_data");
|
string dataFolder = Path.Combine(extractedFolder, "actual_data");
|
||||||
@ -784,7 +784,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
||||||
|
|
||||||
// Update via the beatmap, not the beatmap info, to ensure correct linking
|
// Update via the beatmap, not the beatmap info, to ensure correct linking
|
||||||
@ -814,7 +814,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
var osu = LoadOsuIntoHost(host);
|
var osu = LoadOsuIntoHost(host);
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string temp = TestResources.GetTestBeatmapForImport();
|
||||||
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
||||||
|
|
||||||
BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
|
BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
|
||||||
@ -905,7 +905,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
|
|
||||||
public static async Task<BeatmapSetInfo> LoadQuickOszIntoOsu(OsuGameBase osu)
|
public static async Task<BeatmapSetInfo> LoadQuickOszIntoOsu(OsuGameBase osu)
|
||||||
{
|
{
|
||||||
var temp = TestResources.GetQuickTestBeatmapForImport();
|
string temp = TestResources.GetQuickTestBeatmapForImport();
|
||||||
|
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
@ -920,7 +920,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
|
|
||||||
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
|
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
|
||||||
{
|
{
|
||||||
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
|
string temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
|
||||||
|
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
Assert.AreEqual("this line is gone", bufferedReader.ReadLine());
|
Assert.AreEqual("this line is gone", bufferedReader.ReadLine());
|
||||||
Assert.AreEqual("this one shouldn't be", bufferedReader.PeekLine());
|
Assert.AreEqual("this one shouldn't be", bufferedReader.PeekLine());
|
||||||
|
|
||||||
var endingLines = bufferedReader.ReadToEnd().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
string[] endingLines = bufferedReader.ReadToEnd().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
Assert.AreEqual(3, endingLines.Length);
|
Assert.AreEqual(3, endingLines.Length);
|
||||||
Assert.AreEqual("this one shouldn't be", endingLines[0]);
|
Assert.AreEqual("this one shouldn't be", endingLines[0]);
|
||||||
Assert.AreEqual("these ones", endingLines[1]);
|
Assert.AreEqual("these ones", endingLines[1]);
|
||||||
|
@ -36,8 +36,8 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
"Soleily - Renatus (MMzz) [Muzukashii].osu",
|
"Soleily - Renatus (MMzz) [Muzukashii].osu",
|
||||||
"Soleily - Renatus (MMzz) [Oni].osu"
|
"Soleily - Renatus (MMzz) [Oni].osu"
|
||||||
};
|
};
|
||||||
var maps = reader.Filenames.ToArray();
|
string[] maps = reader.Filenames.ToArray();
|
||||||
foreach (var map in expected)
|
foreach (string map in expected)
|
||||||
Assert.Contains(map, maps);
|
Assert.Contains(map, maps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,8 +77,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var reader = new ZipArchiveReader(osz);
|
var reader = new ZipArchiveReader(osz);
|
||||||
|
|
||||||
using (var stream = new StreamReader(
|
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
||||||
reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
|
||||||
{
|
{
|
||||||
Assert.AreEqual("osu file format v13", stream.ReadLine()?.Trim());
|
Assert.AreEqual("osu file format v13", stream.ReadLine()?.Trim());
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var tempPath = TestResources.GetTestBeatmapForImport();
|
string? tempPath = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
ILive<RealmBeatmapSet>? importedSet;
|
ILive<RealmBeatmapSet>? importedSet;
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -193,7 +193,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -245,7 +245,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -293,7 +293,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -391,7 +391,7 @@ namespace osu.Game.Tests.Database
|
|||||||
checkBeatmapCount(realmFactory.Context, 12);
|
checkBeatmapCount(realmFactory.Context, 12);
|
||||||
checkSingleReferencedFileCount(realmFactory.Context, 18);
|
checkSingleReferencedFileCount(realmFactory.Context, 18);
|
||||||
|
|
||||||
var brokenTempFilename = TestResources.GetTestBeatmapForImport();
|
string? brokenTempFilename = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
MemoryStream brokenOsu = new MemoryStream();
|
MemoryStream brokenOsu = new MemoryStream();
|
||||||
MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename));
|
MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename));
|
||||||
@ -522,7 +522,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
using (File.OpenRead(temp))
|
using (File.OpenRead(temp))
|
||||||
await importer.Import(temp);
|
await importer.Import(temp);
|
||||||
ensureLoaded(realmFactory.Context);
|
ensureLoaded(realmFactory.Context);
|
||||||
@ -539,7 +539,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
Directory.CreateDirectory(extractedFolder);
|
Directory.CreateDirectory(extractedFolder);
|
||||||
@ -575,7 +575,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
string subfolder = Path.Combine(extractedFolder, "subfolder");
|
string subfolder = Path.Combine(extractedFolder, "subfolder");
|
||||||
@ -617,7 +617,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
string extractedFolder = $"{temp}_extracted";
|
string extractedFolder = $"{temp}_extracted";
|
||||||
string dataFolder = Path.Combine(extractedFolder, "actual_data");
|
string dataFolder = Path.Combine(extractedFolder, "actual_data");
|
||||||
@ -668,7 +668,7 @@ namespace osu.Game.Tests.Database
|
|||||||
using var importer = new BeatmapImporter(realmFactory, storage);
|
using var importer = new BeatmapImporter(realmFactory, storage);
|
||||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
await importer.Import(temp);
|
await importer.Import(temp);
|
||||||
|
|
||||||
// Update via the beatmap, not the beatmap info, to ensure correct linking
|
// Update via the beatmap, not the beatmap info, to ensure correct linking
|
||||||
@ -685,7 +685,7 @@ namespace osu.Game.Tests.Database
|
|||||||
|
|
||||||
public static async Task<RealmBeatmapSet?> LoadQuickOszIntoOsu(BeatmapImporter importer, Realm realm)
|
public static async Task<RealmBeatmapSet?> LoadQuickOszIntoOsu(BeatmapImporter importer, Realm realm)
|
||||||
{
|
{
|
||||||
var temp = TestResources.GetQuickTestBeatmapForImport();
|
string? temp = TestResources.GetQuickTestBeatmapForImport();
|
||||||
|
|
||||||
var importedSet = await importer.Import(new ImportTask(temp));
|
var importedSet = await importer.Import(new ImportTask(temp));
|
||||||
|
|
||||||
@ -700,7 +700,7 @@ namespace osu.Game.Tests.Database
|
|||||||
|
|
||||||
public static async Task<RealmBeatmapSet> LoadOszIntoStore(BeatmapImporter importer, Realm realm, string? path = null, bool virtualTrack = false)
|
public static async Task<RealmBeatmapSet> LoadOszIntoStore(BeatmapImporter importer, Realm realm, string? path = null, bool virtualTrack = false)
|
||||||
{
|
{
|
||||||
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
|
string? temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
|
||||||
|
|
||||||
var importedSet = await importer.Import(new ImportTask(temp));
|
var importedSet = await importer.Import(new ImportTask(temp));
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ using osu.Game.Rulesets;
|
|||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
using static osu.Game.Skinning.LegacySkinConfiguration;
|
using static osu.Game.Skinning.SkinConfiguration;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Gameplay
|
namespace osu.Game.Tests.Gameplay
|
||||||
{
|
{
|
||||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
for (int i = 0; i * beat_length_denominator < barLines.Count; i++)
|
for (int i = 0; i * beat_length_denominator < barLines.Count; i++)
|
||||||
{
|
{
|
||||||
var barLine = barLines[i * beat_length_denominator];
|
var barLine = barLines[i * beat_length_denominator];
|
||||||
var expectedTime = beat_length_numerator * (int)signature * i;
|
int expectedTime = beat_length_numerator * (int)signature * i;
|
||||||
|
|
||||||
// every seventh bar's start time should be at least greater than the whole number we expect.
|
// every seventh bar's start time should be at least greater than the whole number we expect.
|
||||||
// It cannot be less, as that can affect overlapping scroll algorithms
|
// It cannot be less, as that can affect overlapping scroll algorithms
|
||||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
File.WriteAllText(actualTestFile, "test");
|
File.WriteAllText(actualTestFile, "test");
|
||||||
|
|
||||||
var rulesetStorage = storage.GetStorageForDirectory("rulesets");
|
var rulesetStorage = storage.GetStorageForDirectory("rulesets");
|
||||||
var lookupPath = rulesetStorage.GetFiles(".").Single();
|
string lookupPath = rulesetStorage.GetFiles(".").Single();
|
||||||
|
|
||||||
Assert.That(lookupPath, Is.EqualTo("test"));
|
Assert.That(lookupPath, Is.EqualTo("test"));
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
|
|
||||||
Assert.That(osuStorage, Is.Not.Null);
|
Assert.That(osuStorage, Is.Not.Null);
|
||||||
|
|
||||||
foreach (var file in osuStorage.IgnoreFiles)
|
foreach (string file in osuStorage.IgnoreFiles)
|
||||||
{
|
{
|
||||||
// avoid touching realm files which may be a pipe and break everything.
|
// avoid touching realm files which may be a pipe and break everything.
|
||||||
// this is also done locally inside OsuStorage via the IgnoreFiles list.
|
// this is also done locally inside OsuStorage via the IgnoreFiles list.
|
||||||
@ -149,7 +149,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
Assert.That(storage.Exists(file), Is.False);
|
Assert.That(storage.Exists(file), Is.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var dir in osuStorage.IgnoreDirectories)
|
foreach (string dir in osuStorage.IgnoreDirectories)
|
||||||
{
|
{
|
||||||
Assert.That(Directory.Exists(Path.Combine(originalDirectory, dir)));
|
Assert.That(Directory.Exists(Path.Combine(originalDirectory, dir)));
|
||||||
Assert.That(storage.ExistsDirectory(dir), Is.False);
|
Assert.That(storage.ExistsDirectory(dir), Is.False);
|
||||||
|
@ -347,7 +347,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < 1000; i++)
|
for (int i = 0; i < 1000; i++)
|
||||||
{
|
{
|
||||||
var time = handler.SetFrameFromTime(destination);
|
double? time = handler.SetFrameFromTime(destination);
|
||||||
if (time == null || time == destination)
|
if (time == null || time == destination)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
Assert.Throws<InvalidOperationException>(() => _ = queue.Dequeue());
|
Assert.Throws<InvalidOperationException>(() => _ = queue.Dequeue());
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
foreach (var _ in queue)
|
foreach (int _ in queue)
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
Assert.AreEqual(0, count);
|
Assert.AreEqual(0, count);
|
||||||
@ -50,7 +50,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
Assert.AreEqual(i, queue[i]);
|
Assert.AreEqual(i, queue[i]);
|
||||||
|
|
||||||
int j = 0;
|
int j = 0;
|
||||||
foreach (var item in queue)
|
foreach (int item in queue)
|
||||||
Assert.AreEqual(j++, item);
|
Assert.AreEqual(j++, item);
|
||||||
|
|
||||||
for (int i = queue.Count; i < queue.Count + capacity; i++)
|
for (int i = queue.Count; i < queue.Count + capacity; i++)
|
||||||
@ -71,7 +71,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
Assert.AreEqual(count - capacity + i, queue[i]);
|
Assert.AreEqual(count - capacity + i, queue[i]);
|
||||||
|
|
||||||
int j = count - capacity;
|
int j = count - capacity;
|
||||||
foreach (var item in queue)
|
foreach (int item in queue)
|
||||||
Assert.AreEqual(j++, item);
|
Assert.AreEqual(j++, item);
|
||||||
|
|
||||||
for (int i = queue.Count; i < queue.Count + capacity; i++)
|
for (int i = queue.Count; i < queue.Count + capacity; i++)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user