mirror of
https://github.com/ppy/osu.git
synced 2026-05-14 01:32:40 +08:00
Compare commits
1 Commits
@@ -21,7 +21,7 @@
|
||||
]
|
||||
},
|
||||
"ppy.localisationanalyser.tools": {
|
||||
"version": "2025.1208.0",
|
||||
"version": "2024.802.0",
|
||||
"commands": [
|
||||
"localisation"
|
||||
]
|
||||
|
||||
@@ -19,11 +19,6 @@ indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# temporary workaround for https://youtrack.jetbrains.com/issue/RIDER-130051/Cannot-resolve-symbol-inspections-incorrectly-firing-for-xmldoc-protected-member-references
|
||||
resharper_c_sharp_warnings_cs1574_cs1584_cs1581_cs1580_highlighting = hint
|
||||
# temporary workaround for https://youtrack.jetbrains.com/issue/RIDER-130381/Rider-does-not-respect-propagated-NoWarn-CS1591?backToIssues=false
|
||||
dotnet_diagnostic.CS1591.severity = none
|
||||
|
||||
#license header
|
||||
file_header_template = Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.\nSee the LICENCE file in the repository root for full licence text.
|
||||
|
||||
|
||||
@@ -38,12 +38,8 @@ jobs:
|
||||
run: ./UseLocalOsu.sh
|
||||
working-directory: ./osu-tools
|
||||
|
||||
- name: Build tools
|
||||
run: dotnet build PerformanceCalculator --nologo --verbosity quiet
|
||||
working-directory: ./osu-tools
|
||||
|
||||
- name: Regenerate mod definitions
|
||||
run: dotnet run --project PerformanceCalculator --no-build -- mods > ../osu-web/database/mods.json
|
||||
run: dotnet run --project PerformanceCalculator -- mods > ../osu-web/database/mods.json
|
||||
working-directory: ./osu-tools
|
||||
|
||||
- name: Create pull request with changes
|
||||
|
||||
+3
-4
@@ -55,7 +55,9 @@ When in doubt, it's probably best to start with a discussion first. We will esca
|
||||
|
||||
While pull requests from unaffiliated contributors are welcome, please note that due to significant community interest and limited review throughput, the core team's primary focus is on the issues which are currently [on the roadmap](https://github.com/orgs/ppy/projects/7/views/6). Reviewing PRs that fall outside of the scope of the roadmap is done on a best-effort basis, so please be aware that it may take a while before a core maintainer gets around to review your change.
|
||||
|
||||
The [issue tracker](https://github.com/ppy/osu/issues) should provide plenty of issues to start with. In the case of simple issues, a direct PR is okay. However, if you decide to work on an existing issue which doesn't seem trivial, **please ask us first**. This way we can try to estimate if it is a good fit for you and provide the correct direction on how to address it. In addition, note that while we do not rule out external contributors from working on roadmapped issues, we will generally prefer to handle them ourselves unless they're not very time sensitive.
|
||||
The [issue tracker](https://github.com/ppy/osu/issues) should provide plenty of issues to start with. We also have a [`good first issue`](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label, although from experience it is not used very often, as it is relatively rare that we can spot an issue that will definitively be a good first issue for a new contributor regardless of their programming experience.
|
||||
|
||||
In the case of simple issues, a direct PR is okay. However, if you decide to work on an existing issue which doesn't seem trivial, **please ask us first**. This way we can try to estimate if it is a good fit for you and provide the correct direction on how to address it. In addition, note that while we do not rule out external contributors from working on roadmapped issues, we will generally prefer to handle them ourselves unless they're not very time sensitive.
|
||||
|
||||
If you'd like to propose a subjective change to one of the visual aspects of the game, or there is a bigger task you'd like to work on, but there is no corresponding issue or discussion thread yet for it, **please open a discussion or issue first** to avoid wasted effort. This in particular applies if you want to work on [one of the available designs from the osu! Figma master library](https://www.figma.com/file/VIkXMYNPMtQem2RJg9k2iQ/Master-Library).
|
||||
|
||||
@@ -71,9 +73,6 @@ Aside from the above, below is a brief checklist of things to watch out when you
|
||||
After you're done with your changes and you wish to open the PR, please observe the following recommendations:
|
||||
|
||||
- Please submit the pull request from a [topic branch](https://git-scm.com/book/en/v2/Git-Branching-Branching-Workflows#_topic_branch) (not `master`), and keep the *Allow edits from maintainers* check box selected, so that we can push fixes to your PR if necessary.
|
||||
- Please pick the following target branch for your pull request:
|
||||
- `pp-dev`, if the change impacts star rating or performance points calculations for any of the rulesets,
|
||||
- `master`, otherwise.
|
||||
- Please avoid pushing untested or incomplete code.
|
||||
- Please do not force-push or rebase unless we ask you to.
|
||||
- Please do not merge `master` continually if there are no conflicts to resolve. We will do this for you when the change is ready for merge.
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageReference Include="NUnit" Version="4.5.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />
|
||||
|
||||
+3
-3
@@ -19,13 +19,13 @@ namespace osu.Game.Rulesets.EmptyFreeform
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
return new DifficultyAttributes(mods, 0);
|
||||
}
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods) => Enumerable.Empty<DifficultyHitObject>();
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => Array.Empty<Skill>();
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty<Skill>();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageReference Include="NUnit" Version="4.5.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||
|
||||
+3
-3
@@ -19,13 +19,13 @@ namespace osu.Game.Rulesets.Pippidon
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
return new DifficultyAttributes(mods, 0);
|
||||
}
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods) => Enumerable.Empty<DifficultyHitObject>();
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => Array.Empty<Skill>();
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty<Skill>();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageReference Include="NUnit" Version="4.5.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />
|
||||
|
||||
+3
-3
@@ -19,13 +19,13 @@ namespace osu.Game.Rulesets.EmptyScrolling
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
return new DifficultyAttributes(mods, 0);
|
||||
}
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods) => Enumerable.Empty<DifficultyHitObject>();
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => Array.Empty<Skill>();
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty<Skill>();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageReference Include="NUnit" Version="4.5.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||
|
||||
+3
-3
@@ -19,13 +19,13 @@ namespace osu.Game.Rulesets.Pippidon
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
return new DifficultyAttributes(mods, 0);
|
||||
}
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods) => Enumerable.Empty<DifficultyHitObject>();
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => Array.Empty<Skill>();
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty<Skill>();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2026.310.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2025.908.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
||||
@@ -5,13 +5,16 @@
|
||||
android:supportsRtl="true"
|
||||
android:label="osu!"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher">
|
||||
<provider android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="sh.ppy.osulazer.fileprovider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
</application>
|
||||
android:roundIcon="@mipmap/ic_launcher" />
|
||||
<!-- for editor usage -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<!--
|
||||
READ_MEDIA_* permissions are available only on API 33 or greater. Devices with older android versions
|
||||
don't understand the new permissions, so request the old READ_EXTERNAL_STORAGE permission to get storage access.
|
||||
Since the old permission has no effect on >= API 33, don't request it.
|
||||
|
||||
Care needs to be taken to ensure runtime permission checks target the correct permission for the API level.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
|
||||
</manifest>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<!-- https://developer.android.com/reference/androidx/core/content/FileProvider -->
|
||||
<external-files-path path="logs" name="logs" />
|
||||
<external-files-path path="exports" name="exports" />
|
||||
</paths>
|
||||
@@ -189,7 +189,7 @@ namespace osu.Desktop
|
||||
}
|
||||
|
||||
// user party
|
||||
if (!hideIdentifiableInformation && multiplayerClient.Room != null && multiplayerClient.Room.Settings.MatchType != MatchType.Matchmaking)
|
||||
if (!hideIdentifiableInformation && multiplayerClient.Room != null)
|
||||
{
|
||||
MultiplayerRoom room = multiplayerClient.Room;
|
||||
|
||||
|
||||
@@ -146,13 +146,9 @@ namespace osu.Desktop
|
||||
{
|
||||
base.SetHost(host);
|
||||
|
||||
// Apple operating systems use a better icon provided via external assets.
|
||||
if (!RuntimeInfo.IsApple)
|
||||
{
|
||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||
if (iconStream != null)
|
||||
host.Window.SetIconFromStream(iconStream);
|
||||
}
|
||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||
if (iconStream != null)
|
||||
host.Window.SetIconFromStream(iconStream);
|
||||
|
||||
host.Window.Title = Name;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
|
||||
@@ -33,7 +32,7 @@ namespace osu.Desktop.Security
|
||||
{
|
||||
public ElevatedPrivilegesNotification()
|
||||
{
|
||||
Text = NotificationsStrings.ElevatedPrivileges(RuntimeInfo.IsUnix ? "root" : "Administrator");
|
||||
Text = $"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance, may break integrations and poses a security risk. Please run the game as a normal user.";
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="System.IO.Packaging" Version="10.0.5" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.6.1.70" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="9.0.2" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageReference Include="Velopack" Version="0.0.1298" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
|
||||
@@ -5,7 +5,7 @@ using BenchmarkDotNet.Attributes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Tests.NonVisual.Filtering;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
|
||||
namespace osu.Game.Benchmarks
|
||||
{
|
||||
@@ -42,7 +42,7 @@ namespace osu.Game.Benchmarks
|
||||
Status = BeatmapOnlineStatus.Loved
|
||||
};
|
||||
|
||||
private FilterMatchingTest.CarouselBeatmap carouselBeatmap = null!;
|
||||
private CarouselBeatmap carouselBeatmap = null!;
|
||||
private FilterCriteria criteria1 = null!;
|
||||
private FilterCriteria criteria2 = null!;
|
||||
private FilterCriteria criteria3 = null!;
|
||||
@@ -55,7 +55,7 @@ namespace osu.Game.Benchmarks
|
||||
var beatmap = getExampleBeatmap();
|
||||
beatmap.OnlineID = 20201010;
|
||||
beatmap.BeatmapSet = new BeatmapSetInfo { OnlineID = 1535 };
|
||||
carouselBeatmap = new FilterMatchingTest.CarouselBeatmap(beatmap);
|
||||
carouselBeatmap = new CarouselBeatmap(beatmap);
|
||||
criteria1 = new FilterCriteria();
|
||||
criteria2 = new FilterCriteria
|
||||
{
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Benchmarks
|
||||
{
|
||||
public class BenchmarkDifficultyCalculation : BenchmarkTest
|
||||
{
|
||||
private DifficultyCalculator osuCalculator = null!;
|
||||
private DifficultyCalculator taikoCalculator = null!;
|
||||
private DifficultyCalculator catchCalculator = null!;
|
||||
private DifficultyCalculator maniaCalculator = null!;
|
||||
|
||||
public override void SetUp()
|
||||
{
|
||||
using var resources = new DllResourceStore(typeof(TestResources).Assembly);
|
||||
|
||||
using var archive = resources.GetStream("Resources/Archives/241526 Soleily - Renatus.osz");
|
||||
using var archiveReader = new ZipArchiveReader(archive);
|
||||
|
||||
var osuBeatmap = readBeatmap(archiveReader, "Soleily - Renatus (Gamu) [Insane].osu");
|
||||
var taikoBeatmap = readBeatmap(archiveReader, "Soleily - Renatus (MMzz) [Oni].osu");
|
||||
var catchBeatmap = readBeatmap(archiveReader, "Soleily - Renatus (Deif) [Salad].osu");
|
||||
var maniaBeatmap = readBeatmap(archiveReader, "Soleily - Renatus (ExPew) [Another].osu");
|
||||
|
||||
osuCalculator = new OsuRuleset().CreateDifficultyCalculator(osuBeatmap);
|
||||
taikoCalculator = new TaikoRuleset().CreateDifficultyCalculator(taikoBeatmap);
|
||||
catchCalculator = new CatchRuleset().CreateDifficultyCalculator(catchBeatmap);
|
||||
maniaCalculator = new ManiaRuleset().CreateDifficultyCalculator(maniaBeatmap);
|
||||
}
|
||||
|
||||
private WorkingBeatmap readBeatmap(ZipArchiveReader archiveReader, string beatmapName)
|
||||
{
|
||||
using var beatmapStream = new MemoryStream();
|
||||
archiveReader.GetStream(beatmapName).CopyTo(beatmapStream);
|
||||
|
||||
beatmapStream.Seek(0, SeekOrigin.Begin);
|
||||
using var reader = new LineBufferedReader(beatmapStream);
|
||||
|
||||
var decoder = Beatmaps.Formats.Decoder.GetDecoder<Beatmap>(reader);
|
||||
return new FlatWorkingBeatmap(decoder.Decode(reader));
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void CalculateDifficultyOsu() => osuCalculator.Calculate();
|
||||
|
||||
[Benchmark]
|
||||
public void CalculateDifficultyTaiko() => taikoCalculator.Calculate();
|
||||
|
||||
[Benchmark]
|
||||
public void CalculateDifficultyCatch() => catchCalculator.Calculate();
|
||||
|
||||
[Benchmark]
|
||||
public void CalculateDifficultyMania() => maniaCalculator.Calculate();
|
||||
|
||||
[Benchmark]
|
||||
public void CalculateDifficultyOsuHundredTimes()
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
osuCalculator.Calculate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
|
||||
<PackageReference Include="nunit" Version="4.5.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageReference Include="nunit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -35,9 +35,11 @@
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/AppIcon.appiconset</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
@@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Rulesets.Catch.Skinning;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Legacy;
|
||||
@@ -22,9 +21,9 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
var skinSource = new SkinProvidingContainer(rawSkin);
|
||||
var skin = new CatchLegacySkinTransformer(skinSource);
|
||||
|
||||
ClassicAssert.AreEqual(new Color4(232, 185, 35, 255), skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value);
|
||||
ClassicAssert.AreEqual(new Color4(232, 74, 35, 255), skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value);
|
||||
ClassicAssert.AreEqual(new Color4(0, 255, 255, 255), skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashFruit)?.Value);
|
||||
Assert.AreEqual(new Color4(232, 185, 35, 255), skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value);
|
||||
Assert.AreEqual(new Color4(232, 74, 35, 255), skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value);
|
||||
Assert.AreEqual(new Color4(0, 255, 255, 255), skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashFruit)?.Value);
|
||||
}
|
||||
|
||||
private class TestLegacySkin : LegacySkin
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTinyDropletMissChangesCatcherState()
|
||||
public void TestTinyDropletMissPreservesCatcherState()
|
||||
{
|
||||
AddStep("catch hyper kiai fruit", () => attemptCatch(new TestKiaiFruit
|
||||
{
|
||||
@@ -165,8 +165,8 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
}));
|
||||
AddStep("catch tiny droplet", () => attemptCatch(new TinyDroplet()));
|
||||
AddStep("miss tiny droplet", () => attemptCatch(new TinyDroplet { X = 100 }));
|
||||
// catcher state is changed but hyper dash state is preserved
|
||||
checkState(CatcherAnimationState.Fail);
|
||||
// catcher state and hyper dash state is preserved
|
||||
checkState(CatcherAnimationState.Kiai);
|
||||
checkHyperDash(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageReference Include="NUnit" Version="4.5.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
||||
@@ -176,20 +176,15 @@ namespace osu.Game.Rulesets.Catch
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetCatch };
|
||||
|
||||
public override IEnumerable<HitResult> GetValidHitResults()
|
||||
protected override IEnumerable<HitResult> GetValidHitResults()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
HitResult.Great,
|
||||
HitResult.Miss,
|
||||
|
||||
HitResult.LargeTickHit,
|
||||
HitResult.LargeTickMiss,
|
||||
HitResult.SmallTickHit,
|
||||
HitResult.SmallTickMiss,
|
||||
HitResult.LargeBonus,
|
||||
HitResult.IgnoreHit,
|
||||
HitResult.IgnoreMiss,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -305,7 +300,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
Description = "Affects how early fruits fade in on the screen.",
|
||||
AdditionalMetrics =
|
||||
[
|
||||
new RulesetBeatmapAttribute.AdditionalMetric("Fade-in time", LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRangeInt(effectiveDifficulty.ApproachRate, CatchHitObject.PREEMPT_RANGE):#,0.##} ms"))
|
||||
new RulesetBeatmapAttribute.AdditionalMetric("Fade-in time", LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRange(effectiveDifficulty.ApproachRate, CatchHitObject.PREEMPT_RANGE):#,0.##} ms"))
|
||||
]
|
||||
};
|
||||
yield return new RulesetBeatmapAttribute(SongSelectStrings.HPDrain, @"HP", originalDifficulty.DrainRate, effectiveDifficulty.DrainRate, 10)
|
||||
|
||||
@@ -15,7 +15,6 @@ using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
@@ -23,14 +22,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
private const double difficulty_multiplier = 4.59;
|
||||
|
||||
public override int Version => 20251020;
|
||||
private float halfCatcherWidth;
|
||||
|
||||
public override int Version => 20250306;
|
||||
|
||||
public CatchDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
if (beatmap.HitObjects.Count == 0)
|
||||
return new CatchDifficultyAttributes { Mods = mods };
|
||||
@@ -45,19 +46,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
return attributes;
|
||||
}
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods)
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||
{
|
||||
CatchHitObject? lastObject = null;
|
||||
|
||||
List<DifficultyHitObject> objects = new List<DifficultyHitObject>();
|
||||
|
||||
double clockRate = ModUtils.CalculateRateWithMods(mods);
|
||||
|
||||
float halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.Difficulty) * 0.5f;
|
||||
|
||||
// For circle sizes above 5.5, reduce the catcher width further to simulate imperfect gameplay.
|
||||
halfCatcherWidth *= 1 - (Math.Max(0, beatmap.Difficulty.CircleSize - 5.5f) * 0.0625f);
|
||||
|
||||
// In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream.
|
||||
foreach (var hitObject in CatchBeatmap.GetPalpableObjects(beatmap.HitObjects))
|
||||
{
|
||||
@@ -74,11 +68,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
return objects;
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.Difficulty) * 0.5f;
|
||||
|
||||
// For circle sizes above 5.5, reduce the catcher width further to simulate imperfect gameplay.
|
||||
halfCatcherWidth *= 1 - (Math.Max(0, beatmap.Difficulty.CircleSize - 5.5f) * 0.0625f);
|
||||
|
||||
return new Skill[]
|
||||
{
|
||||
new Movement(mods),
|
||||
new Movement(mods, halfCatcherWidth, clockRate),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Scoring.Legacy;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
@@ -51,13 +51,15 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
// Combo scaling
|
||||
if (catchAttributes.MaxCombo > 0)
|
||||
value *= Math.Min(Math.Pow(score.MaxCombo, 0.35) / Math.Pow(catchAttributes.MaxCombo, 0.35), 1.0);
|
||||
value *= Math.Min(Math.Pow(score.MaxCombo, 0.8) / Math.Pow(catchAttributes.MaxCombo, 0.8), 1.0);
|
||||
|
||||
var difficulty = score.BeatmapInfo!.Difficulty.Clone();
|
||||
|
||||
score.Mods.OfType<IApplicableToDifficulty>().ForEach(m => m.ApplyToDifficulty(difficulty));
|
||||
|
||||
double clockRate = ModUtils.CalculateRateWithMods(score.Mods);
|
||||
var track = new TrackVirtual(10000);
|
||||
score.Mods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
|
||||
double clockRate = track.Rate;
|
||||
|
||||
// this is the same as osu!, so there's potential to share the implementation... maybe
|
||||
double preempt = IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty.Evaluators
|
||||
{
|
||||
public static class MovementEvaluator
|
||||
{
|
||||
private const double direction_change_bonus = 21.0;
|
||||
|
||||
public static double EvaluateDifficultyOf(DifficultyHitObject current)
|
||||
{
|
||||
var catchCurrent = (CatchDifficultyHitObject)current;
|
||||
var catchLast = (CatchDifficultyHitObject)current.Previous(0);
|
||||
var catchLastLast = (CatchDifficultyHitObject)current.Previous(1);
|
||||
|
||||
// In catch, clockrate adjustments do not only affect the timings of hitobjects,
|
||||
// but also the speed of the player's catcher, which has an impact on difficulty
|
||||
double catcherSpeedMultiplier = current.ClockRate;
|
||||
|
||||
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catcherSpeedMultiplier);
|
||||
|
||||
double distanceAddition = (Math.Pow(Math.Abs(catchCurrent.DistanceMoved), 1.3) / 510);
|
||||
double sqrtStrain = Math.Sqrt(weightedStrainTime);
|
||||
|
||||
double edgeDashBonus = 0;
|
||||
|
||||
// Direction change bonus.
|
||||
if (Math.Abs(catchCurrent.DistanceMoved) > 0.1)
|
||||
{
|
||||
if (current.Index >= 1 && Math.Abs(catchLast.DistanceMoved) > 0.1 && Math.Sign(catchCurrent.DistanceMoved) != Math.Sign(catchLast.DistanceMoved))
|
||||
{
|
||||
double bonusFactor = Math.Min(50, Math.Abs(catchCurrent.DistanceMoved)) / 50;
|
||||
double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(catchLast.DistanceMoved)) / 70, 0.38);
|
||||
|
||||
distanceAddition += direction_change_bonus / Math.Sqrt(catchLast.StrainTime + 16) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0);
|
||||
}
|
||||
|
||||
// Base bonus for every movement, giving some weight to streams.
|
||||
distanceAddition += 12.5 * Math.Min(Math.Abs(catchCurrent.DistanceMoved), CatchDifficultyHitObject.NORMALIZED_HALF_CATCHER_WIDTH * 2)
|
||||
/ (CatchDifficultyHitObject.NORMALIZED_HALF_CATCHER_WIDTH * 6) / sqrtStrain;
|
||||
}
|
||||
|
||||
// Linear spacing nerf.
|
||||
double linearSpacingCount = 0;
|
||||
|
||||
for (int i = 0; i < Math.Min(current.Index, 10); i++)
|
||||
{
|
||||
var catchPrevObj = (CatchDifficultyHitObject)catchCurrent.Previous(i);
|
||||
|
||||
// Only same direction movements matter as they do not take any additional inputs.
|
||||
if (Math.Sign(catchCurrent.DistanceMoved) != Math.Sign(catchPrevObj.DistanceMoved) || catchCurrent.DistanceMoved == 0 || catchPrevObj.DistanceMoved == 0)
|
||||
break;
|
||||
|
||||
double currentSpacing = Math.Abs(catchCurrent.DistanceMoved / catchCurrent.StrainTime);
|
||||
double prevSpacing = Math.Abs(catchPrevObj.DistanceMoved / catchPrevObj.StrainTime);
|
||||
|
||||
double relativeDifference = Math.Abs(currentSpacing / prevSpacing - 1);
|
||||
|
||||
if (relativeDifference > 0.05)
|
||||
break;
|
||||
|
||||
linearSpacingCount++;
|
||||
}
|
||||
|
||||
distanceAddition *= Math.Pow(0.7, linearSpacingCount);
|
||||
|
||||
// Bonus for edge dashes.
|
||||
if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f)
|
||||
{
|
||||
if (!catchCurrent.LastObject.HyperDash)
|
||||
edgeDashBonus += 5.7;
|
||||
|
||||
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20)
|
||||
* Math.Pow((Math.Min(catchCurrent.StrainTime * catcherSpeedMultiplier, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
||||
}
|
||||
|
||||
// There is an edge case where horizontal back and forth sliders create "buzz" patterns which are repeated "movements" with a distance lower than
|
||||
// the platter's width but high enough to be considered a movement due to the absolute_player_positioning_error and NORMALIZED_HALF_CATCHER_WIDTH offsets
|
||||
// We are detecting this exact scenario. The first back and forth is counted but all subsequent ones are nullified.
|
||||
// To achieve that, we need to store the exact distances (distance ignoring absolute_player_positioning_error and NORMALIZED_HALF_CATCHER_WIDTH)
|
||||
if (current.Index >= 2 && Math.Abs(catchCurrent.ExactDistanceMoved) <= CatchDifficultyHitObject.NORMALIZED_HALF_CATCHER_WIDTH * 2
|
||||
&& catchCurrent.ExactDistanceMoved == -catchLast.ExactDistanceMoved && catchLast.ExactDistanceMoved == -catchLastLast.ExactDistanceMoved
|
||||
&& catchCurrent.StrainTime == catchLast.StrainTime && catchLast.StrainTime == catchLastLast.StrainTime)
|
||||
distanceAddition = 0;
|
||||
|
||||
return distanceAddition / weightedStrainTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,49 +11,15 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
||||
{
|
||||
public class CatchDifficultyHitObject : DifficultyHitObject
|
||||
{
|
||||
public const float NORMALIZED_HALF_CATCHER_WIDTH = 41.0f;
|
||||
private const float absolute_player_positioning_error = 16.0f;
|
||||
private const float normalized_hitobject_radius = 41.0f;
|
||||
|
||||
public new PalpableCatchHitObject BaseObject => (PalpableCatchHitObject)base.BaseObject;
|
||||
|
||||
public new PalpableCatchHitObject LastObject => (PalpableCatchHitObject)base.LastObject;
|
||||
|
||||
/// <summary>
|
||||
/// Normalized position of <see cref="BaseObject"/>.
|
||||
/// </summary>
|
||||
public readonly float NormalizedPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Normalized position of <see cref="LastObject"/>.
|
||||
/// </summary>
|
||||
public readonly float LastNormalizedPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Normalized position of the player required to catch <see cref="BaseObject"/>, assuming the player moves as little as possible.
|
||||
/// </summary>
|
||||
public float PlayerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized position of the player after catching <see cref="LastObject"/>.
|
||||
/// </summary>
|
||||
public float LastPlayerPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized distance between <see cref="LastPlayerPosition"/> and <see cref="PlayerPosition"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The sign of the value indicates the direction of the movement: negative is left and positive is right.
|
||||
/// </remarks>
|
||||
public float DistanceMoved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized distance the player has to move from <see cref="LastPlayerPosition"/> in order to catch <see cref="BaseObject"/> at its <see cref="NormalizedPosition"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The sign of the value indicates the direction of the movement: negative is left and positive is right.
|
||||
/// </remarks>
|
||||
public float ExactDistanceMoved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds elapsed since the start time of the previous <see cref="CatchDifficultyHitObject"/>, with a minimum of 40ms.
|
||||
/// </summary>
|
||||
@@ -63,35 +29,13 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
||||
: base(hitObject, lastObject, clockRate, objects, index)
|
||||
{
|
||||
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||
float scalingFactor = NORMALIZED_HALF_CATCHER_WIDTH / halfCatcherWidth;
|
||||
float scalingFactor = normalized_hitobject_radius / halfCatcherWidth;
|
||||
|
||||
NormalizedPosition = BaseObject.EffectiveX * scalingFactor;
|
||||
LastNormalizedPosition = LastObject.EffectiveX * scalingFactor;
|
||||
|
||||
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
||||
StrainTime = Math.Max(40, DeltaTime);
|
||||
|
||||
setMovementState();
|
||||
}
|
||||
|
||||
private void setMovementState()
|
||||
{
|
||||
LastPlayerPosition = Index == 0 ? LastNormalizedPosition : ((CatchDifficultyHitObject)Previous(0)).PlayerPosition;
|
||||
|
||||
PlayerPosition = Math.Clamp(
|
||||
LastPlayerPosition,
|
||||
NormalizedPosition - (NORMALIZED_HALF_CATCHER_WIDTH - absolute_player_positioning_error),
|
||||
NormalizedPosition + (NORMALIZED_HALF_CATCHER_WIDTH - absolute_player_positioning_error)
|
||||
);
|
||||
|
||||
DistanceMoved = PlayerPosition - LastPlayerPosition;
|
||||
|
||||
// For the exact position we consider that the catcher is in the correct position for both objects
|
||||
ExactDistanceMoved = NormalizedPosition - LastPlayerPosition;
|
||||
|
||||
// After a hyperdash we ARE in the correct position. Always!
|
||||
if (LastObject.HyperDash)
|
||||
PlayerPosition = NormalizedPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Catch.Difficulty.Evaluators;
|
||||
using System;
|
||||
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -10,6 +11,10 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
{
|
||||
public class Movement : StrainDecaySkill
|
||||
{
|
||||
private const float absolute_player_positioning_error = 16f;
|
||||
private const float normalized_hitobject_radius = 41.0f;
|
||||
private const double direction_change_bonus = 21.0;
|
||||
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 0.2;
|
||||
|
||||
@@ -17,14 +22,107 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
|
||||
protected override int SectionLength => 750;
|
||||
|
||||
public Movement(Mod[] mods)
|
||||
protected readonly float HalfCatcherWidth;
|
||||
|
||||
private float? lastPlayerPosition;
|
||||
private float lastDistanceMoved;
|
||||
private float lastExactDistanceMoved;
|
||||
private double lastStrainTime;
|
||||
private bool isInBuzzSection;
|
||||
|
||||
/// <summary>
|
||||
/// The speed multiplier applied to the player's catcher.
|
||||
/// </summary>
|
||||
private readonly double catcherSpeedMultiplier;
|
||||
|
||||
public Movement(Mod[] mods, float halfCatcherWidth, double clockRate)
|
||||
: base(mods)
|
||||
{
|
||||
HalfCatcherWidth = halfCatcherWidth;
|
||||
|
||||
// In catch, clockrate adjustments do not only affect the timings of hitobjects,
|
||||
// but also the speed of the player's catcher, which has an impact on difficulty
|
||||
// TODO: Support variable clockrates caused by mods such as ModTimeRamp
|
||||
// (perhaps by using IApplicableToRate within the CatchDifficultyHitObject constructor to set a catcher speed for each object before processing)
|
||||
catcherSpeedMultiplier = clockRate;
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
return MovementEvaluator.EvaluateDifficultyOf(current);
|
||||
var catchCurrent = (CatchDifficultyHitObject)current;
|
||||
|
||||
lastPlayerPosition ??= catchCurrent.LastNormalizedPosition;
|
||||
|
||||
float playerPosition = Math.Clamp(
|
||||
lastPlayerPosition.Value,
|
||||
catchCurrent.NormalizedPosition - (normalized_hitobject_radius - absolute_player_positioning_error),
|
||||
catchCurrent.NormalizedPosition + (normalized_hitobject_radius - absolute_player_positioning_error)
|
||||
);
|
||||
|
||||
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
||||
|
||||
// For the exact position we consider that the catcher is in the correct position for both objects
|
||||
float exactDistanceMoved = catchCurrent.NormalizedPosition - lastPlayerPosition.Value;
|
||||
|
||||
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catcherSpeedMultiplier);
|
||||
|
||||
double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
|
||||
double sqrtStrain = Math.Sqrt(weightedStrainTime);
|
||||
|
||||
double edgeDashBonus = 0;
|
||||
|
||||
// Direction change bonus.
|
||||
if (Math.Abs(distanceMoved) > 0.1)
|
||||
{
|
||||
if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved))
|
||||
{
|
||||
double bonusFactor = Math.Min(50, Math.Abs(distanceMoved)) / 50;
|
||||
double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.38);
|
||||
|
||||
distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 16) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0);
|
||||
}
|
||||
|
||||
// Base bonus for every movement, giving some weight to streams.
|
||||
distanceAddition += 12.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain;
|
||||
}
|
||||
|
||||
// Bonus for edge dashes.
|
||||
if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f)
|
||||
{
|
||||
if (!catchCurrent.LastObject.HyperDash)
|
||||
edgeDashBonus += 5.7;
|
||||
else
|
||||
{
|
||||
// After a hyperdash we ARE in the correct position. Always!
|
||||
playerPosition = catchCurrent.NormalizedPosition;
|
||||
}
|
||||
|
||||
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20)
|
||||
* Math.Pow((Math.Min(catchCurrent.StrainTime * catcherSpeedMultiplier, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
||||
}
|
||||
|
||||
// There is an edge case where horizontal back and forth sliders create "buzz" patterns which are repeated "movements" with a distance lower than
|
||||
// the platter's width but high enough to be considered a movement due to the absolute_player_positioning_error and normalized_hitobject_radius offsets
|
||||
// We are detecting this exact scenario. The first back and forth is counted but all subsequent ones are nullified.
|
||||
// To achieve that, we need to store the exact distances (distance ignoring absolute_player_positioning_error and normalized_hitobject_radius)
|
||||
if (Math.Abs(exactDistanceMoved) <= HalfCatcherWidth * 2 && exactDistanceMoved == -lastExactDistanceMoved && catchCurrent.StrainTime == lastStrainTime)
|
||||
{
|
||||
if (isInBuzzSection)
|
||||
distanceAddition = 0;
|
||||
else
|
||||
isInBuzzSection = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isInBuzzSection = false;
|
||||
}
|
||||
|
||||
lastPlayerPosition = playerPosition;
|
||||
lastDistanceMoved = distanceMoved;
|
||||
lastStrainTime = catchCurrent.StrainTime;
|
||||
lastExactDistanceMoved = exactDistanceMoved;
|
||||
|
||||
return distanceAddition / weightedStrainTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||
private double placementStartTime;
|
||||
private double placementEndTime;
|
||||
|
||||
protected override bool IsValidForPlacement => base.IsValidForPlacement && (PlacementActive == PlacementState.Waiting || Precision.DefinitelyBigger(HitObject.Duration, 0));
|
||||
protected override bool IsValidForPlacement => Precision.DefinitelyBigger(HitObject.Duration, 0);
|
||||
|
||||
public BananaShowerPlacementBlueprint()
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||
|
||||
private InputManager inputManager = null!;
|
||||
|
||||
protected override bool IsValidForPlacement => base.IsValidForPlacement && (PlacementActive == PlacementState.Waiting || Precision.DefinitelyBigger(HitObject.Duration, 0));
|
||||
protected override bool IsValidForPlacement => Precision.DefinitelyBigger(HitObject.Duration, 0);
|
||||
|
||||
public JuiceStreamPlacementBlueprint()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using osu.Framework.Allocation;
|
||||
@@ -225,8 +224,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
#region Clipboard handling
|
||||
|
||||
public override string ConvertSelectionToString()
|
||||
=> string.Join(',', EditorBeatmap.SelectedHitObjects.Cast<CatchHitObject>().OrderBy(h => h.StartTime)
|
||||
.Select(h => (h.IndexInCurrentCombo + 1).ToString(CultureInfo.InvariantCulture)));
|
||||
=> string.Join(',', EditorBeatmap.SelectedHitObjects.Cast<CatchHitObject>().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString()));
|
||||
|
||||
// 1,2,3,4 ...
|
||||
private static readonly Regex selection_regex = new Regex(@"^\d+(,\d+)*$", RegexOptions.Compiled);
|
||||
|
||||
@@ -51,8 +51,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
|
||||
return string.Empty;
|
||||
|
||||
string format(string acronym, DifficultyBindable bindable)
|
||||
=> $"{acronym}{bindable.Value!.Value.ToStandardFormattedString(1)}";
|
||||
string format(string acronym, DifficultyBindable bindable) => $"{acronym}{bindable.Value!.Value.ToStandardFormattedString(1)}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@@ -54,25 +53,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
public override IEnumerable<string> LookupNames => lookup_names;
|
||||
|
||||
public BananaHitSampleInfo()
|
||||
: this(string.Empty)
|
||||
public BananaHitSampleInfo(int volume = 100)
|
||||
: base(string.Empty, volume: volume)
|
||||
{
|
||||
}
|
||||
|
||||
public BananaHitSampleInfo(HitSampleInfo info)
|
||||
: this(info.Name, info.Bank, info.Suffix, info.Volume, info.EditorAutoBank, info.UseBeatmapSamples)
|
||||
{
|
||||
}
|
||||
|
||||
private BananaHitSampleInfo(string name, string bank = SampleControlPoint.DEFAULT_BANK, string? suffix = null, int volume = 100, bool editorAutoBank = true, bool useBeatmapSamples = false)
|
||||
: base(name, bank, suffix, volume, editorAutoBank, useBeatmapSamples)
|
||||
{
|
||||
}
|
||||
|
||||
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default,
|
||||
Optional<bool> newEditorAutoBank = default, Optional<bool> newUseBeatmapSamples = default)
|
||||
=> new BananaHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newSuffix.GetOr(Suffix), newVolume.GetOr(Volume),
|
||||
newEditorAutoBank.GetOr(EditorAutoBank), newUseBeatmapSamples.GetOr(UseBeatmapSamples));
|
||||
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default, Optional<bool> newEditorAutoBank = default)
|
||||
=> new BananaHitSampleInfo(newVolume.GetOr(Volume));
|
||||
|
||||
public bool Equals(BananaHitSampleInfo? other)
|
||||
=> other != null;
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
StartTime = time,
|
||||
BananaIndex = count,
|
||||
Samples = new List<HitSampleInfo> { new Banana.BananaHitSampleInfo(CreateHitSampleInfo()) }
|
||||
Samples = new List<HitSampleInfo> { new Banana.BananaHitSampleInfo(CreateHitSampleInfo().Volume) }
|
||||
});
|
||||
|
||||
count++;
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimePreempt = IBeatmapDifficultyInfo.DifficultyRangeInt(difficulty.ApproachRate, PREEMPT_RANGE);
|
||||
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_RANGE);
|
||||
|
||||
Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize);
|
||||
}
|
||||
|
||||
@@ -72,9 +72,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
leaderboard.Origin = Anchor.CentreLeft;
|
||||
leaderboard.X = 10;
|
||||
}
|
||||
|
||||
foreach (var d in container.OfType<ISerialisableDrawable>())
|
||||
d.UsesFixedAnchor = true;
|
||||
})
|
||||
{
|
||||
Children = new Drawable[]
|
||||
|
||||
@@ -224,20 +224,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
addLighting(result, drawableObject.AccentColour.Value, positionInStack.X);
|
||||
}
|
||||
|
||||
if (result.IsHit)
|
||||
CurrentState = hitObject.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle;
|
||||
else if (hitObject is not Banana)
|
||||
CurrentState = CatcherAnimationState.Fail;
|
||||
|
||||
if (palpableObject.HitObject.LastInCombo)
|
||||
{
|
||||
if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result))
|
||||
Explode();
|
||||
else
|
||||
Drop();
|
||||
}
|
||||
|
||||
// droplet doesn't affect hyperdash state
|
||||
// droplet doesn't affect the catcher state
|
||||
if (hitObject is TinyDroplet) return;
|
||||
|
||||
// if a hyper fruit was already handled this frame, just go where it says to go.
|
||||
@@ -257,6 +244,19 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
else
|
||||
SetHyperDashState();
|
||||
}
|
||||
|
||||
if (result.IsHit)
|
||||
CurrentState = hitObject.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle;
|
||||
else if (!(hitObject is Banana))
|
||||
CurrentState = CatcherAnimationState.Fail;
|
||||
|
||||
if (palpableObject.HitObject.LastInCombo)
|
||||
{
|
||||
if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result))
|
||||
Explode();
|
||||
else
|
||||
Drop();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRevertResult(JudgementResult result)
|
||||
|
||||
@@ -35,9 +35,11 @@
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/AppIcon.appiconset</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
@@ -1,351 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
using DragArea = osu.Game.Screens.Edit.Compose.Components.Timeline.TimelineHitObjectBlueprint.DragArea;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
{
|
||||
public partial class TestSceneHoldNoteTailDrag : EditorTestScene
|
||||
{
|
||||
protected override Ruleset CreateEditorRuleset() => new ManiaRuleset();
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
AddStep("Clear objects", () => EditorBeatmap.Clear());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSimpleTailDragForward()
|
||||
{
|
||||
AddStep("Add hold note", () =>
|
||||
{
|
||||
EditorBeatmap.Add(new HoldNote { StartTime = 2170, Duration = 937.5 });
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().Single();
|
||||
dragForward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Duration is higher", () => ((HoldNote)EditorBeatmap.HitObjects.First())!.Duration > 937.5f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSimpleTailDragBackwards()
|
||||
{
|
||||
AddStep("Add hold note", () =>
|
||||
{
|
||||
EditorBeatmap.Add(new HoldNote { StartTime = 2170, Duration = 937.5 });
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().Single();
|
||||
dragBackward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Duration is lower", () => ((HoldNote)EditorBeatmap.HitObjects[0]).Duration < 937.5f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSamePositionButNotSelectedDragForward()
|
||||
{
|
||||
AddStep("Add hold notes", () =>
|
||||
{
|
||||
EditorBeatmap.AddRange([
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 0 },
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 1 }
|
||||
]);
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
dragForward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Duration is higher, other is unchanged", () =>
|
||||
((HoldNote)EditorBeatmap.HitObjects[0]).Duration > 937.5f &&
|
||||
((HoldNote)EditorBeatmap.HitObjects[^1]).Duration == 937.5f
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSamePositionButNotSelectedDragBackward()
|
||||
{
|
||||
AddStep("Add hold notes", () =>
|
||||
{
|
||||
EditorBeatmap.AddRange([
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 0 },
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 1 }
|
||||
]);
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
dragBackward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Duration is lower, other is unchanged", () =>
|
||||
((HoldNote)EditorBeatmap.HitObjects[0]).Duration < 937.5f &&
|
||||
((HoldNote)EditorBeatmap.HitObjects[^1]).Duration == 937.5f
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSamePositionSelectedDragForward()
|
||||
{
|
||||
AddStep("Add hold notes", () =>
|
||||
{
|
||||
EditorBeatmap.AddRange([
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 0 },
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 1 }
|
||||
]);
|
||||
});
|
||||
|
||||
AddStep("Select all", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects);
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
dragForward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Both durations are higher", () =>
|
||||
((HoldNote)EditorBeatmap.HitObjects[0]).Duration > 937.5f &&
|
||||
((HoldNote)EditorBeatmap.HitObjects[^1]).Duration > 937.5f
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSamePositionSelectedDragBackward()
|
||||
{
|
||||
AddStep("Add hold notes", () =>
|
||||
{
|
||||
EditorBeatmap.AddRange([
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 0 },
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 1 }
|
||||
]);
|
||||
});
|
||||
|
||||
AddStep("Select all", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects);
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
dragBackward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Both durations are lower", () =>
|
||||
((HoldNote)EditorBeatmap.HitObjects[0]).Duration < 937.5f &&
|
||||
((HoldNote)EditorBeatmap.HitObjects[^1]).Duration < 937.5f
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectedButDifferentPositions()
|
||||
{
|
||||
AddStep("Add hold notes", () =>
|
||||
{
|
||||
EditorBeatmap.AddRange([
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 0 },
|
||||
new HoldNote { StartTime = 2404, Duration = 937.5, Column = 1 }
|
||||
]);
|
||||
});
|
||||
|
||||
AddStep("Select all", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects);
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
dragBackward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Duration is unchanged, other is lower", () =>
|
||||
((HoldNote)EditorBeatmap.HitObjects[0]).Duration == 937.5f &&
|
||||
((HoldNote)EditorBeatmap.HitObjects[^1]).Duration < 937.5f
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectedSameStartTimeDifferentDurations()
|
||||
{
|
||||
AddStep("Add hold notes", () =>
|
||||
{
|
||||
EditorBeatmap.AddRange([
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 0 },
|
||||
new HoldNote { StartTime = 2170, Duration = 1171.8, Column = 1 }
|
||||
]);
|
||||
});
|
||||
|
||||
AddStep("Select all", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects);
|
||||
});
|
||||
|
||||
AddStep("Drag until both match", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
InputManager.MoveMouseTo(blueprintDragArea);
|
||||
InputManager.PressKey(Key.LShift);
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.MoveMouseTo(new Vector2(1000, 110));
|
||||
});
|
||||
|
||||
AddStep("Continue the drag", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
dragBackward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Duration is unchanged, other is lower", () =>
|
||||
((HoldNote)EditorBeatmap.HitObjects[0]).Duration == 937.5f &&
|
||||
((HoldNote)EditorBeatmap.HitObjects[^1]).Duration < 937.5f
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectedSameDurationDifferentStartTimes()
|
||||
{
|
||||
AddStep("Add hold notes", () =>
|
||||
{
|
||||
EditorBeatmap.AddRange([
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 0 },
|
||||
new HoldNote { StartTime = 2638.7, Duration = 937.5, Column = 1 }
|
||||
]);
|
||||
});
|
||||
|
||||
AddStep("Select all", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects);
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
dragBackward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Duration is unchanged, other is lower", () =>
|
||||
((HoldNote)EditorBeatmap.HitObjects[0]).Duration == 937.5f &&
|
||||
((HoldNote)EditorBeatmap.HitObjects[^1]).Duration < 937.5f
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDragNoteOutsideOfSelection()
|
||||
{
|
||||
AddStep("Add hold notes", () =>
|
||||
{
|
||||
EditorBeatmap.AddRange([
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 0 },
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 1 }
|
||||
]);
|
||||
});
|
||||
|
||||
AddStep("Select the back stack slider", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.Last());
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
dragBackward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Duration is lower, other is unchanged", () =>
|
||||
((HoldNote)EditorBeatmap.HitObjects[0]).Duration < 937.5f &&
|
||||
((HoldNote)EditorBeatmap.HitObjects[^1]).Duration == 937.5f
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDragHoldNoteWithNotes()
|
||||
{
|
||||
AddStep("Add notes", () =>
|
||||
{
|
||||
EditorBeatmap.AddRange([
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 0 },
|
||||
new Note { StartTime = 2170, Column = 1 },
|
||||
new Note { StartTime = 3107.5, Column = 2 },
|
||||
new HoldNote { StartTime = 2170, Duration = 937.5, Column = 3 }
|
||||
]);
|
||||
});
|
||||
|
||||
AddStep("Select all", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects);
|
||||
});
|
||||
|
||||
AddStep("Drag tail", () =>
|
||||
{
|
||||
var blueprintDragArea = this.ChildrenOfType<DragArea>().First();
|
||||
dragBackward(blueprintDragArea);
|
||||
});
|
||||
|
||||
AddStep("Release tail", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("Both durations are lower", () =>
|
||||
{
|
||||
var holdNotes = EditorBeatmap.HitObjects.OfType<HoldNote>();
|
||||
return holdNotes.First().Duration < 937.5f && holdNotes.Last().Duration < 937.5f;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void dragForward(DragArea dragArea)
|
||||
{
|
||||
InputManager.MoveMouseTo(dragArea);
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.MoveMouseTo(new Vector2(1100, 110));
|
||||
}
|
||||
|
||||
private void dragBackward(DragArea dragArea)
|
||||
{
|
||||
InputManager.MoveMouseTo(dragArea);
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.MoveMouseTo(new Vector2(700, 110));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@@ -17,7 +16,6 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK.Input;
|
||||
|
||||
@@ -38,8 +36,21 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
[Test]
|
||||
public void TestPlaceBeforeCurrentTimeDownwards()
|
||||
{
|
||||
AddStep("seek to 200", () => HitObjectContainer.Dependencies.Get<EditorClock>().Seek(200));
|
||||
AddStep("move mouse before current time", () =>
|
||||
{
|
||||
var column = this.ChildrenOfType<Column>().Single();
|
||||
InputManager.MoveMouseTo(column.ScreenSpacePositionAtTime(-100));
|
||||
});
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("note start time < 0", () => getNote().StartTime < 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlaceAfterCurrentTimeDownwards()
|
||||
{
|
||||
AddStep("move mouse after current time", () =>
|
||||
{
|
||||
var column = this.ChildrenOfType<Column>().Single();
|
||||
InputManager.MoveMouseTo(column.ScreenSpacePositionAtTime(100));
|
||||
@@ -47,22 +58,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("note start time < 200", () => getNote().StartTime < 200);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlaceAfterCurrentTimeDownwards()
|
||||
{
|
||||
AddStep("seek to 200", () => HitObjectContainer.Dependencies.Get<EditorClock>().Seek(200));
|
||||
AddStep("move mouse after current time", () =>
|
||||
{
|
||||
var column = this.ChildrenOfType<Column>().Single();
|
||||
InputManager.MoveMouseTo(column.ScreenSpacePositionAtTime(300));
|
||||
});
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("note start time > 200", () => getNote().StartTime > 200);
|
||||
AddAssert("note start time > 0", () => getNote().StartTime > 0);
|
||||
}
|
||||
|
||||
private Note getNote() => this.ChildrenOfType<DrawableNote>().FirstOrDefault()?.HitObject;
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
AddStep("change seek setting to true", () => config.SetValue(OsuSetting.EditorAutoSeekOnPlacement, true));
|
||||
placeObject();
|
||||
AddUntilStep("wait for seek to complete", () => !EditorClock.IsSeeking);
|
||||
AddAssert("seeked forward to object", () => EditorClock.CurrentTime, () => Is.GreaterThan(initialTime!));
|
||||
AddAssert("seeked forward to object", () => EditorClock.CurrentTime, () => Is.GreaterThan(initialTime));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -18,11 +18,15 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
public void TestNormalSelection()
|
||||
{
|
||||
addStepClickLink("00:05:920 (5920|3,6623|3,6857|2,7326|1)");
|
||||
AddAssert("selected group", () => checkSnapAndSelectColumn(5_920, [(5_920, 3), (6_623, 3), (6_857, 2), (7_326, 1)]));
|
||||
AddAssert("selected group", () => checkSnapAndSelectColumn(5_920, new List<(int, int)>
|
||||
{ (5_920, 3), (6_623, 3), (6_857, 2), (7_326, 1) }
|
||||
));
|
||||
|
||||
addReset();
|
||||
addStepClickLink("00:42:716 (42716|3,43420|2,44123|0,44357|1,45295|1)");
|
||||
AddAssert("selected ungrouped", () => checkSnapAndSelectColumn(42_716, [(42_716, 3), (43_420, 2), (44_123, 0), (44_357, 1), (45_295, 1)]));
|
||||
AddAssert("selected ungrouped", () => checkSnapAndSelectColumn(42_716, new List<(int, int)>
|
||||
{ (42_716, 3), (43_420, 2), (44_123, 0), (44_357, 1), (45_295, 1) }
|
||||
));
|
||||
|
||||
addReset();
|
||||
AddStep("add notes to row", () =>
|
||||
@@ -37,20 +41,15 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
EditorBeatmap.AddRange(new[] { second, third, forth });
|
||||
});
|
||||
addStepClickLink("00:11:545 (11545|0,11545|1,11545|2,11545|3)");
|
||||
AddAssert("selected in row", () => checkSnapAndSelectColumn(11_545, [(11_545, 0), (11_545, 1), (11_545, 2), (11_545, 3)]));
|
||||
AddAssert("selected in row", () => checkSnapAndSelectColumn(11_545, new List<(int, int)>
|
||||
{ (11_545, 0), (11_545, 1), (11_545, 2), (11_545, 3) }
|
||||
));
|
||||
|
||||
addReset();
|
||||
addStepClickLink("01:36:623 (96623|1,97560|1,97677|1,97795|1,98966|1)");
|
||||
AddAssert("selected in column", () => checkSnapAndSelectColumn(96_623, [(96_623, 1), (97_560, 1), (97_677, 1), (97_795, 1), (98_966, 1)]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRoundingToNearestMillisecondApplied()
|
||||
{
|
||||
AddStep("resnap note to have fractional coordinates",
|
||||
() => EditorBeatmap.HitObjects.OfType<ManiaHitObject>().Single(ho => ho.StartTime == 85_373 && ho.Column == 1).StartTime = 85_373.125);
|
||||
addStepClickLink("01:25:373 (85373|1)");
|
||||
AddAssert("selected note", () => checkSnapAndSelectColumn(85_373.125, [(85_373.125, 1)]));
|
||||
AddAssert("selected in column", () => checkSnapAndSelectColumn(96_623, new List<(int, int)>
|
||||
{ (96_623, 1), (97_560, 1), (97_677, 1), (97_795, 1), (98_966, 1) }
|
||||
));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -76,7 +75,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
|
||||
private void addReset() => addStepClickLink("00:00:000", "reset", false);
|
||||
|
||||
private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(double, int)>? columnPairs = null)
|
||||
private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(int, int)>? columnPairs = null)
|
||||
{
|
||||
bool checkColumns = columnPairs != null
|
||||
? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2)))
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
[TestCase("mania-samples")]
|
||||
[TestCase("mania-slider")] // e.g. second and fourth notes of https://osu.ppy.sh/beatmapsets/73883#mania/216407
|
||||
[TestCase("slider-convert-samples")]
|
||||
[TestCase("spinner-convert-samples")]
|
||||
public void Test(string name) => base.Test(name);
|
||||
|
||||
protected override IEnumerable<SampleConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Screens.Select;
|
||||
@@ -19,19 +18,19 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
criteria.TryParseCustomKeywordCriteria("keys", Operator.Equal, "1");
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 1 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 2 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 3 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new RulesetInfo { OnlineID = 0 }, new BeatmapDifficulty { CircleSize = 4 }),
|
||||
new FilterCriteria
|
||||
{
|
||||
@@ -45,19 +44,19 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
criteria.TryParseCustomKeywordCriteria("keys", Operator.Equal, "1,3,5,7");
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 1 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 2 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 3 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new RulesetInfo { OnlineID = 0 }, new BeatmapDifficulty { CircleSize = 4 }),
|
||||
new FilterCriteria
|
||||
{
|
||||
@@ -71,19 +70,19 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
criteria.TryParseCustomKeywordCriteria("keys", Operator.NotEqual, "1");
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 1 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 2 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 3 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new RulesetInfo { OnlineID = 0 }, new BeatmapDifficulty { CircleSize = 4 }),
|
||||
new FilterCriteria
|
||||
{
|
||||
@@ -97,19 +96,19 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
criteria.TryParseCustomKeywordCriteria("keys", Operator.NotEqual, "1,3,5,7");
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 1 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 2 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 3 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new RulesetInfo { OnlineID = 0 }, new BeatmapDifficulty { CircleSize = 4 }),
|
||||
new FilterCriteria
|
||||
{
|
||||
@@ -123,23 +122,23 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
criteria.TryParseCustomKeywordCriteria("keys", Operator.GreaterOrEqual, "4");
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 1 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 2 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 4 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 5 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new RulesetInfo { OnlineID = 0 }, new BeatmapDifficulty { CircleSize = 3 }),
|
||||
new FilterCriteria
|
||||
{
|
||||
@@ -154,23 +153,23 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
criteria.TryParseCustomKeywordCriteria("keys", Operator.Greater, "4");
|
||||
criteria.TryParseCustomKeywordCriteria("keys", Operator.NotEqual, "7");
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 3 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 4 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 5 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.False(criteria.Matches(
|
||||
Assert.False(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 7 }),
|
||||
new FilterCriteria()));
|
||||
|
||||
ClassicAssert.True(criteria.Matches(
|
||||
Assert.True(criteria.Matches(
|
||||
new BeatmapInfo(new ManiaRuleset().RulesetInfo, new BeatmapDifficulty { CircleSize = 9 }),
|
||||
new FilterCriteria()));
|
||||
}
|
||||
@@ -180,9 +179,9 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
|
||||
ClassicAssert.False(criteria.TryParseCustomKeywordCriteria("keys", Operator.Equal, "some text"));
|
||||
ClassicAssert.False(criteria.TryParseCustomKeywordCriteria("keys", Operator.NotEqual, "4,some text"));
|
||||
ClassicAssert.False(criteria.TryParseCustomKeywordCriteria("keys", Operator.GreaterOrEqual, "4,5,6"));
|
||||
Assert.False(criteria.TryParseCustomKeywordCriteria("keys", Operator.Equal, "some text"));
|
||||
Assert.False(criteria.TryParseCustomKeywordCriteria("keys", Operator.NotEqual, "4,some text"));
|
||||
Assert.False(criteria.TryParseCustomKeywordCriteria("keys", Operator.GreaterOrEqual, "4,5,6"));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
@@ -200,7 +199,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 0,
|
||||
EndTimeObjectCount = 0
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo1, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo1, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "0");
|
||||
BeatmapInfo beatmapInfo2 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
@@ -208,7 +207,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 0
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo2, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo2, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "100");
|
||||
BeatmapInfo beatmapInfo3 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
@@ -216,7 +215,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 100
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo3, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo3, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "1");
|
||||
BeatmapInfo beatmapInfo4 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
@@ -224,7 +223,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 1
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo4, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo4, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "0.1");
|
||||
BeatmapInfo beatmapInfo5 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
@@ -232,7 +231,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 1000,
|
||||
EndTimeObjectCount = 1
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo5, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo5, filterCriteria));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
@@ -250,7 +249,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 0,
|
||||
EndTimeObjectCount = 0
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo1, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo1, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "0");
|
||||
BeatmapInfo beatmapInfo2 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
@@ -258,7 +257,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 0
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo2, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo2, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "100");
|
||||
BeatmapInfo beatmapInfo3 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
@@ -266,7 +265,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 100
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo3, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo3, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "1");
|
||||
BeatmapInfo beatmapInfo4 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
@@ -274,7 +273,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 1
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo4, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo4, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "0.1");
|
||||
BeatmapInfo beatmapInfo5 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
@@ -282,7 +281,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 1000,
|
||||
EndTimeObjectCount = 1
|
||||
};
|
||||
ClassicAssert.True(criteria.Matches(beatmapInfo5, filterCriteria));
|
||||
Assert.True(criteria.Matches(beatmapInfo5, filterCriteria));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
@@ -300,7 +299,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 50
|
||||
};
|
||||
ClassicAssert.False(criteria.Matches(beatmapInfo, filterCriteria));
|
||||
Assert.False(criteria.Matches(beatmapInfo, filterCriteria));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
@@ -308,8 +307,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
|
||||
ClassicAssert.False(criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "some text"));
|
||||
ClassicAssert.False(criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "1some text"));
|
||||
Assert.False(criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "some text"));
|
||||
Assert.False(criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "1some text"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
@@ -36,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
var definition = new StageDefinition(columns);
|
||||
var results = getResults(definition);
|
||||
ClassicAssert.AreEqual(special, results);
|
||||
Assert.AreEqual(special, results);
|
||||
}
|
||||
|
||||
private IEnumerable<bool> getResults(StageDefinition definition)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Tests.Visual;
|
||||
@@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||
public void TestMapHasNoHoldNotes()
|
||||
{
|
||||
var testBeatmap = createModdedBeatmap();
|
||||
ClassicAssert.False(testBeatmap.HitObjects.OfType<HoldNote>().Any());
|
||||
Assert.False(testBeatmap.HitObjects.OfType<HoldNote>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
Mode: 0
|
||||
|
||||
[TimingPoints]
|
||||
0,300,4,0,2,100,1,0
|
||||
|
||||
[HitObjects]
|
||||
444,320,1000,5,2,0:0:0:0:
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 1000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 1000.0,
|
||||
"EndTime": 8000.0,
|
||||
"Column": 0,
|
||||
"PlaySlidingSamples": false,
|
||||
"NodeSamples": [
|
||||
["Gameplay/soft-hitnormal"],
|
||||
["Gameplay/soft-hitnormal", "Gameplay/soft-hitfinish"]
|
||||
],
|
||||
"Samples": ["Gameplay/soft-hitnormal", "Gameplay/soft-hitfinish"],
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
Mode: 0
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:5
|
||||
CircleSize:5
|
||||
OverallDifficulty:5
|
||||
ApproachRate:5
|
||||
SliderMultiplier:1.4
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
0,500,4,2,0,100,1,0
|
||||
|
||||
[HitObjects]
|
||||
256,192,1000,8,4,8000,0:2:0:0:
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
@@ -34,11 +33,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
ClassicAssert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
|
||||
ClassicAssert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||
ClassicAssert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
|
||||
Assert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
|
||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -55,11 +54,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
ClassicAssert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
|
||||
ClassicAssert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||
ClassicAssert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
|
||||
Assert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
|
||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -75,11 +74,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
ClassicAssert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
|
||||
ClassicAssert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||
ClassicAssert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
||||
Assert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
|
||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -97,13 +96,13 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
ClassicAssert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
|
||||
Assert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
|
||||
|
||||
ClassicAssert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||
ClassicAssert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
|
||||
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -120,15 +119,15 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
ClassicAssert.AreEqual(generated.Frames.Count, frame_offset + 4, "Incorrect number of frames");
|
||||
ClassicAssert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
|
||||
ClassicAssert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect first note release time");
|
||||
ClassicAssert.AreEqual(2000, generated.Frames[frame_offset + 2].Time, "Incorrect second note hit time");
|
||||
ClassicAssert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been pressed");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released");
|
||||
Assert.AreEqual(generated.Frames.Count, frame_offset + 4, "Incorrect number of frames");
|
||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
|
||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect first note release time");
|
||||
Assert.AreEqual(2000, generated.Frames[frame_offset + 2].Time, "Incorrect second note hit time");
|
||||
Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -147,16 +146,16 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
ClassicAssert.AreEqual(generated.Frames.Count, frame_offset + 4, "Incorrect number of frames");
|
||||
ClassicAssert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
|
||||
ClassicAssert.AreEqual(3000, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time");
|
||||
ClassicAssert.AreEqual(2000, generated.Frames[frame_offset + 1].Time, "Incorrect second note hit time");
|
||||
ClassicAssert.AreEqual(4000, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key1), "Key1 has not been released");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has been released");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released");
|
||||
Assert.AreEqual(generated.Frames.Count, frame_offset + 4, "Incorrect number of frames");
|
||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
|
||||
Assert.AreEqual(3000, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time");
|
||||
Assert.AreEqual(2000, generated.Frames[frame_offset + 1].Time, "Incorrect second note hit time");
|
||||
Assert.AreEqual(4000, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key1), "Key1 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has been released");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -174,14 +173,14 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
ClassicAssert.AreEqual(generated.Frames.Count, frame_offset + 3, "Incorrect number of frames");
|
||||
ClassicAssert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
|
||||
ClassicAssert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect second note press time + first note release time");
|
||||
ClassicAssert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect second note release time");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
|
||||
ClassicAssert.True(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key2), "Key2 has not been pressed");
|
||||
ClassicAssert.False(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been released");
|
||||
Assert.AreEqual(generated.Frames.Count, frame_offset + 3, "Incorrect number of frames");
|
||||
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
|
||||
Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect second note press time + first note release time");
|
||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect second note release time");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key2), "Key2 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been released");
|
||||
}
|
||||
|
||||
private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action));
|
||||
|
||||
@@ -45,19 +45,5 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
AssertBeatmapLookup(expected_sample);
|
||||
AssertNoLookup(unwanted_sample);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConvertHitObjectCustomSampleBank()
|
||||
{
|
||||
const string beatmap_sample = "normal-hitwhistle2";
|
||||
const string user_skin_sample = "normal-hitnormal";
|
||||
|
||||
SetupSkins(beatmap_sample, user_skin_sample);
|
||||
|
||||
CreateTestWithBeatmap("convert-beatmap-custom-sample-bank.osu");
|
||||
|
||||
AssertBeatmapLookup(beatmap_sample);
|
||||
AssertUserLookup(user_skin_sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageReference Include="NUnit" Version="4.5.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
||||
@@ -85,11 +85,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
Duration = endTime - HitObject.StartTime,
|
||||
Column = column,
|
||||
Samples = HitObject.Samples,
|
||||
NodeSamples =
|
||||
[
|
||||
HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_NORMAL).ToList(),
|
||||
HitObject.Samples
|
||||
]
|
||||
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
|
||||
};
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty.Evaluators
|
||||
{
|
||||
public class IndividualStrainEvaluator
|
||||
{
|
||||
public static double EvaluateDifficultyOf(DifficultyHitObject current)
|
||||
{
|
||||
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
||||
double startTime = maniaCurrent.StartTime;
|
||||
double endTime = maniaCurrent.EndTime;
|
||||
|
||||
double holdFactor = 1.0; // Factor to all additional strains in case something else is held
|
||||
|
||||
// We award a bonus if this note starts and ends before the end of another hold note.
|
||||
foreach (var maniaPrevious in maniaCurrent.PreviousHitObjects)
|
||||
{
|
||||
if (maniaPrevious is null)
|
||||
continue;
|
||||
|
||||
if (Precision.DefinitelyBigger(maniaPrevious.EndTime, endTime, 1) &&
|
||||
Precision.DefinitelyBigger(startTime, maniaPrevious.StartTime, 1))
|
||||
{
|
||||
holdFactor = 1.25;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 2.0 * holdFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty.Evaluators
|
||||
{
|
||||
public class OverallStrainEvaluator
|
||||
{
|
||||
private const double release_threshold = 30;
|
||||
|
||||
public static double EvaluateDifficultyOf(DifficultyHitObject current)
|
||||
{
|
||||
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
||||
double startTime = maniaCurrent.StartTime;
|
||||
double endTime = maniaCurrent.EndTime;
|
||||
bool isOverlapping = false;
|
||||
|
||||
double closestEndTime = Math.Abs(endTime - startTime); // Lowest value we can assume with the current information
|
||||
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
|
||||
|
||||
foreach (var maniaPrevious in maniaCurrent.PreviousHitObjects)
|
||||
{
|
||||
if (maniaPrevious is null)
|
||||
continue;
|
||||
|
||||
// The current note is overlapped if a previous note or end is overlapping the current note body
|
||||
isOverlapping |= Precision.DefinitelyBigger(maniaPrevious.EndTime, startTime, 1) &&
|
||||
Precision.DefinitelyBigger(endTime, maniaPrevious.EndTime, 1) &&
|
||||
Precision.DefinitelyBigger(startTime, maniaPrevious.StartTime, 1);
|
||||
|
||||
// We give a slight bonus to everything if something is held meanwhile
|
||||
if (Precision.DefinitelyBigger(maniaPrevious.EndTime, endTime, 1) &&
|
||||
Precision.DefinitelyBigger(startTime, maniaPrevious.StartTime, 1))
|
||||
holdFactor = 1.25;
|
||||
|
||||
closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - maniaPrevious.EndTime));
|
||||
}
|
||||
|
||||
// The hold addition is given if there was an overlap, however it is only valid if there are no other note with a similar ending.
|
||||
// Releasing multiple notes is just as easy as releasing 1. Nerfs the hold addition by half if the closest release is release_threshold away.
|
||||
// holdAddition
|
||||
// ^
|
||||
// 1.0 + - - - - - -+-----------
|
||||
// | /
|
||||
// 0.5 + - - - - -/ Sigmoid Curve
|
||||
// | /|
|
||||
// 0.0 +--------+-+---------------> Release Difference / ms
|
||||
// release_threshold
|
||||
if (isOverlapping)
|
||||
holdAddition = DifficultyCalculationUtils.Logistic(x: closestEndTime, multiplier: 0.27, midpointOffset: release_threshold);
|
||||
|
||||
return (1 + holdAddition) * holdFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
{
|
||||
@@ -37,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.MatchesOnlineID(ruleset);
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills)
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
if (beatmap.HitObjects.Count == 0)
|
||||
return new ManiaDifficultyAttributes { Mods = mods };
|
||||
@@ -63,27 +62,16 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, Mod[] mods)
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||
{
|
||||
var sortedObjects = beatmap.HitObjects.ToArray();
|
||||
int totalColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||
|
||||
double clockRate = ModUtils.CalculateRateWithMods(mods);
|
||||
|
||||
LegacySortHelper<HitObject>.Sort(sortedObjects, Comparer<HitObject>.Create((a, b) => (int)Math.Round(a.StartTime) - (int)Math.Round(b.StartTime)));
|
||||
|
||||
List<DifficultyHitObject> objects = new List<DifficultyHitObject>();
|
||||
List<DifficultyHitObject>[] perColumnObjects = new List<DifficultyHitObject>[totalColumns];
|
||||
|
||||
for (int column = 0; column < totalColumns; column++)
|
||||
perColumnObjects[column] = new List<DifficultyHitObject>();
|
||||
|
||||
for (int i = 1; i < sortedObjects.Length; i++)
|
||||
{
|
||||
var currentObject = new ManiaDifficultyHitObject(sortedObjects[i], sortedObjects[i - 1], clockRate, objects, perColumnObjects, objects.Count);
|
||||
objects.Add(currentObject);
|
||||
perColumnObjects[currentObject.Column].Add(currentObject);
|
||||
}
|
||||
objects.Add(new ManiaDifficultyHitObject(sortedObjects[i], sortedObjects[i - 1], clockRate, objects, objects.Count));
|
||||
|
||||
return objects;
|
||||
}
|
||||
@@ -91,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
|
||||
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||
{
|
||||
new Strain(mods, ((ManiaBeatmap)Beatmap).TotalColumns)
|
||||
};
|
||||
|
||||
@@ -12,59 +12,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Preprocessing
|
||||
{
|
||||
public new ManiaHitObject BaseObject => (ManiaHitObject)base.BaseObject;
|
||||
|
||||
private readonly List<DifficultyHitObject>[] perColumnObjects;
|
||||
|
||||
private readonly int columnIndex;
|
||||
|
||||
public readonly int Column;
|
||||
|
||||
// The hit object earlier in time than this note in each column
|
||||
public readonly ManiaDifficultyHitObject?[] PreviousHitObjects;
|
||||
|
||||
public readonly double ColumnStrainTime;
|
||||
|
||||
public ManiaDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, List<DifficultyHitObject> objects, List<DifficultyHitObject>[] perColumnObjects, int index)
|
||||
public ManiaDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, List<DifficultyHitObject> objects, int index)
|
||||
: base(hitObject, lastObject, clockRate, objects, index)
|
||||
{
|
||||
int totalColumns = perColumnObjects.Length;
|
||||
this.perColumnObjects = perColumnObjects;
|
||||
Column = BaseObject.Column;
|
||||
columnIndex = perColumnObjects[Column].Count;
|
||||
PreviousHitObjects = new ManiaDifficultyHitObject[totalColumns];
|
||||
ColumnStrainTime = StartTime - PrevInColumn(0)?.StartTime ?? StartTime;
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
ManiaDifficultyHitObject prevNote = (ManiaDifficultyHitObject)objects[index - 1];
|
||||
|
||||
for (int i = 0; i < prevNote.PreviousHitObjects.Length; i++)
|
||||
PreviousHitObjects[i] = prevNote.PreviousHitObjects[i];
|
||||
|
||||
// intentionally depends on processing order to match live.
|
||||
PreviousHitObjects[prevNote.Column] = prevNote;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The previous object in the same column as this <see cref="ManiaDifficultyHitObject"/>, exclusive of Long Note tails.
|
||||
/// </summary>
|
||||
/// <param name="backwardsIndex">The number of notes to go back.</param>
|
||||
/// <returns>The object in this column <paramref name="backwardsIndex"/> notes back, or null if this is the first note in the column.</returns>
|
||||
public ManiaDifficultyHitObject? PrevInColumn(int backwardsIndex)
|
||||
{
|
||||
int index = columnIndex - (backwardsIndex + 1);
|
||||
return index >= 0 && index < perColumnObjects[Column].Count ? (ManiaDifficultyHitObject)perColumnObjects[Column][index] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The next object in the same column as this <see cref="ManiaDifficultyHitObject"/>, exclusive of Long Note tails.
|
||||
/// </summary>
|
||||
/// <param name="forwardsIndex">The number of notes to go forward.</param>
|
||||
/// <returns>The object in this column <paramref name="forwardsIndex"/> notes forward, or null if this is the last note in the column.</returns>
|
||||
public ManiaDifficultyHitObject? NextInColumn(int forwardsIndex)
|
||||
{
|
||||
int index = columnIndex + (forwardsIndex + 1);
|
||||
return index >= 0 && index < perColumnObjects[Column].Count ? (ManiaDifficultyHitObject)perColumnObjects[Column][index] : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Evaluators;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
@@ -14,17 +15,23 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
{
|
||||
private const double individual_decay_base = 0.125;
|
||||
private const double overall_decay_base = 0.30;
|
||||
private const double release_threshold = 30;
|
||||
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 1;
|
||||
|
||||
private readonly double[] startTimes;
|
||||
private readonly double[] endTimes;
|
||||
private readonly double[] individualStrains;
|
||||
private double highestIndividualStrain;
|
||||
|
||||
private double individualStrain;
|
||||
private double overallStrain;
|
||||
|
||||
public Strain(Mod[] mods, int totalColumns)
|
||||
: base(mods)
|
||||
{
|
||||
startTimes = new double[totalColumns];
|
||||
endTimes = new double[totalColumns];
|
||||
individualStrains = new double[totalColumns];
|
||||
overallStrain = 1;
|
||||
}
|
||||
@@ -32,24 +39,65 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
||||
double startTime = maniaCurrent.StartTime;
|
||||
double endTime = maniaCurrent.EndTime;
|
||||
int column = maniaCurrent.BaseObject.Column;
|
||||
bool isOverlapping = false;
|
||||
|
||||
individualStrains[maniaCurrent.Column] = applyDecay(individualStrains[maniaCurrent.Column], maniaCurrent.ColumnStrainTime, individual_decay_base);
|
||||
individualStrains[maniaCurrent.Column] += IndividualStrainEvaluator.EvaluateDifficultyOf(current);
|
||||
double closestEndTime = Math.Abs(endTime - startTime); // Lowest value we can assume with the current information
|
||||
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
|
||||
|
||||
// Take the hardest individualStrain for notes that happen at the same time (in a chord).
|
||||
// This is to ensure the order in which the notes are processed does not affect the resultant total strain.
|
||||
highestIndividualStrain = maniaCurrent.DeltaTime <= 1 ? Math.Max(highestIndividualStrain, individualStrains[maniaCurrent.Column]) : individualStrains[maniaCurrent.Column];
|
||||
for (int i = 0; i < endTimes.Length; ++i)
|
||||
{
|
||||
// The current note is overlapped if a previous note or end is overlapping the current note body
|
||||
isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) &&
|
||||
Precision.DefinitelyBigger(endTime, endTimes[i], 1) &&
|
||||
Precision.DefinitelyBigger(startTime, startTimes[i], 1);
|
||||
|
||||
overallStrain = applyDecay(overallStrain, maniaCurrent.DeltaTime, overall_decay_base);
|
||||
overallStrain += OverallStrainEvaluator.EvaluateDifficultyOf(current);
|
||||
// We give a slight bonus to everything if something is held meanwhile
|
||||
if (Precision.DefinitelyBigger(endTimes[i], endTime, 1) &&
|
||||
Precision.DefinitelyBigger(startTime, startTimes[i], 1))
|
||||
holdFactor = 1.25;
|
||||
|
||||
closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - endTimes[i]));
|
||||
}
|
||||
|
||||
// The hold addition is given if there was an overlap, however it is only valid if there are no other note with a similar ending.
|
||||
// Releasing multiple notes is just as easy as releasing 1. Nerfs the hold addition by half if the closest release is release_threshold away.
|
||||
// holdAddition
|
||||
// ^
|
||||
// 1.0 + - - - - - -+-----------
|
||||
// | /
|
||||
// 0.5 + - - - - -/ Sigmoid Curve
|
||||
// | /|
|
||||
// 0.0 +--------+-+---------------> Release Difference / ms
|
||||
// release_threshold
|
||||
if (isOverlapping)
|
||||
holdAddition = DifficultyCalculationUtils.Logistic(x: closestEndTime, multiplier: 0.27, midpointOffset: release_threshold);
|
||||
|
||||
// Decay and increase individualStrains in own column
|
||||
individualStrains[column] = applyDecay(individualStrains[column], startTime - startTimes[column], individual_decay_base);
|
||||
individualStrains[column] += 2.0 * holdFactor;
|
||||
|
||||
// For notes at the same time (in a chord), the individualStrain should be the hardest individualStrain out of those columns
|
||||
individualStrain = maniaCurrent.DeltaTime <= 1 ? Math.Max(individualStrain, individualStrains[column]) : individualStrains[column];
|
||||
|
||||
// Decay and increase overallStrain
|
||||
overallStrain = applyDecay(overallStrain, current.DeltaTime, overall_decay_base);
|
||||
overallStrain += (1 + holdAddition) * holdFactor;
|
||||
|
||||
// Update startTimes and endTimes arrays
|
||||
startTimes[column] = startTime;
|
||||
endTimes[column] = endTime;
|
||||
|
||||
// By subtracting CurrentStrain, this skill effectively only considers the maximum strain of any one hitobject within each strain section.
|
||||
return highestIndividualStrain + overallStrain - CurrentStrain;
|
||||
return individualStrain + overallStrain - CurrentStrain;
|
||||
}
|
||||
|
||||
protected override double CalculateInitialStrain(double offset, DifficultyHitObject current) =>
|
||||
applyDecay(highestIndividualStrain, offset - current.Previous(0).StartTime, individual_decay_base)
|
||||
+ applyDecay(overallStrain, offset - current.Previous(0).StartTime, overall_decay_base);
|
||||
protected override double CalculateInitialStrain(double offset, DifficultyHitObject current)
|
||||
=> applyDecay(individualStrain, offset - current.Previous(0).StartTime, individual_decay_base)
|
||||
+ applyDecay(overallStrain, offset - current.Previous(0).StartTime, overall_decay_base);
|
||||
|
||||
private double applyDecay(double value, double deltaTime, double decayBase)
|
||||
=> value * Math.Pow(decayBase, deltaTime / 1000);
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
[Resolved]
|
||||
private IScrollingInfo scrollingInfo { get; set; } = null!;
|
||||
|
||||
protected override bool IsValidForPlacement => base.IsValidForPlacement && (PlacementActive == PlacementState.Waiting || Precision.DefinitelyBigger(HitObject.Duration, 0));
|
||||
protected override bool IsValidForPlacement => Precision.DefinitelyBigger(HitObject.Duration, 0);
|
||||
|
||||
public HoldNotePlacementBlueprint()
|
||||
: base(new HoldNote())
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// 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 System.Text.RegularExpressions;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
@@ -56,8 +54,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
};
|
||||
|
||||
public override string ConvertSelectionToString()
|
||||
=> string.Join(',', EditorBeatmap.SelectedHitObjects.Cast<ManiaHitObject>().OrderBy(h => h.StartTime)
|
||||
.Select(h => FormattableString.Invariant($"{Math.Round(h.StartTime)}|{h.Column}")));
|
||||
=> string.Join(',', EditorBeatmap.SelectedHitObjects.Cast<ManiaHitObject>().OrderBy(h => h.StartTime).Select(h => $"{h.StartTime}|{h.Column}"));
|
||||
|
||||
// 123|0,456|1,789|2 ...
|
||||
private static readonly Regex selection_regex = new Regex(@"^\d+\|\d+(,\d+\|\d+)*$", RegexOptions.Compiled);
|
||||
@@ -76,10 +73,10 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
if (split.Length != 2)
|
||||
continue;
|
||||
|
||||
if (!int.TryParse(split[0], out int time) || !int.TryParse(split[1], out int column))
|
||||
if (!double.TryParse(split[0], out double time) || !int.TryParse(split[1], out int column))
|
||||
continue;
|
||||
|
||||
ManiaHitObject? current = remainingHitObjects.FirstOrDefault(h => Precision.AlmostEquals(h.StartTime, time, 0.5) && h.Column == column);
|
||||
ManiaHitObject? current = remainingHitObjects.FirstOrDefault(h => h.StartTime == time && h.Column == column);
|
||||
|
||||
if (current == null)
|
||||
continue;
|
||||
|
||||
@@ -383,7 +383,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderDescending().First(v => variant >= v);
|
||||
}
|
||||
|
||||
public override IEnumerable<HitResult> GetValidHitResults()
|
||||
protected override IEnumerable<HitResult> GetValidHitResults()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
@@ -392,11 +392,9 @@ namespace osu.Game.Rulesets.Mania
|
||||
HitResult.Good,
|
||||
HitResult.Ok,
|
||||
HitResult.Meh,
|
||||
HitResult.Miss,
|
||||
|
||||
HitResult.IgnoreHit,
|
||||
HitResult.ComboBreak,
|
||||
HitResult.IgnoreMiss,
|
||||
// HitResult.SmallBonus is used for awarding perfect bonus score but is not included here as
|
||||
// it would be a bit redundant to show this to the user.
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
@@ -31,45 +31,47 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsItemV2(new FormEnumDropdown<ManiaScrollingDirection>
|
||||
new SettingsEnumDropdown<ManiaScrollingDirection>
|
||||
{
|
||||
Caption = RulesetSettingsStrings.ScrollingDirection,
|
||||
LabelText = RulesetSettingsStrings.ScrollingDirection,
|
||||
Current = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
|
||||
}),
|
||||
new SettingsItemV2(new FormSliderBar<double>
|
||||
},
|
||||
new SettingsSlider<double, ManiaScrollSlider>
|
||||
{
|
||||
Caption = RulesetSettingsStrings.ScrollSpeed,
|
||||
LabelText = RulesetSettingsStrings.ScrollSpeed,
|
||||
Current = config.GetBindable<double>(ManiaRulesetSetting.ScrollSpeed),
|
||||
KeyboardStep = 1,
|
||||
LabelFormat = v => RulesetSettingsStrings.ScrollSpeedTooltip((int)DrawableManiaRuleset.ComputeScrollTime(v), v),
|
||||
}),
|
||||
new SettingsItemV2(new FormCheckBox
|
||||
{
|
||||
Caption = RulesetSettingsStrings.TimingBasedColouring,
|
||||
Current = config.GetBindable<bool>(ManiaRulesetSetting.TimingBasedNoteColouring),
|
||||
})
|
||||
KeyboardStep = 1
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
Keywords = new[] { "color" },
|
||||
LabelText = RulesetSettingsStrings.TimingBasedColouring,
|
||||
Current = config.GetBindable<bool>(ManiaRulesetSetting.TimingBasedNoteColouring),
|
||||
},
|
||||
};
|
||||
|
||||
Add(new SettingsItemV2(new FormCheckBox
|
||||
Add(new SettingsCheckbox
|
||||
{
|
||||
Caption = RulesetSettingsStrings.TouchOverlay,
|
||||
LabelText = RulesetSettingsStrings.TouchOverlay,
|
||||
Current = config.GetBindable<bool>(ManiaRulesetSetting.TouchOverlay)
|
||||
}));
|
||||
});
|
||||
|
||||
if (RuntimeInfo.IsMobile)
|
||||
{
|
||||
Add(new SettingsItemV2(new FormEnumDropdown<ManiaMobileLayout>
|
||||
Add(new SettingsEnumDropdown<ManiaMobileLayout>
|
||||
{
|
||||
Caption = RulesetSettingsStrings.MobileLayout,
|
||||
LabelText = RulesetSettingsStrings.MobileLayout,
|
||||
Current = config.GetBindable<ManiaMobileLayout>(ManiaRulesetSetting.MobileLayout),
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
Items = Enum.GetValues<ManiaMobileLayout>().Where(l => l != ManiaMobileLayout.LandscapeWithOverlay),
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private partial class ManiaScrollSlider : RoundedSliderBar<double>
|
||||
{
|
||||
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip((int)DrawableManiaRuleset.ComputeScrollTime(Current.Value), Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
typeof(ManiaModFadeIn)
|
||||
}).ToArray();
|
||||
|
||||
public override bool Ranked => true;
|
||||
public override bool Ranked => false;
|
||||
|
||||
public override bool ValidForFreestyleAsRequiredMod => false;
|
||||
|
||||
|
||||
@@ -7,15 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModDifficultyAdjust : ModDifficultyAdjust
|
||||
{
|
||||
public override DifficultyBindable OverallDifficulty { get; } = new DifficultyBindable
|
||||
{
|
||||
Precision = 0.1f,
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
// Use larger extended limits for mania to include OD values that occur with EZ or HR enabled
|
||||
ExtendedMaxValue = 15,
|
||||
ExtendedMinValue = -15,
|
||||
ReadCurrentFromDifficulty = diff => diff.OverallDifficulty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
|
||||
@@ -35,8 +34,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||
|
||||
double mostCommonBeatLengthBefore = beatmap.GetMostCommonBeatLength();
|
||||
|
||||
var newObjects = new List<ManiaHitObject>();
|
||||
|
||||
foreach (var h in beatmap.HitObjects.OfType<HoldNote>())
|
||||
@@ -51,17 +48,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
}
|
||||
|
||||
maniaBeatmap.HitObjects = maniaBeatmap.HitObjects.OfType<Note>().Concat(newObjects).OrderBy(h => h.StartTime).ToList();
|
||||
|
||||
double mostCommonBeatLengthAfter = beatmap.GetMostCommonBeatLength();
|
||||
|
||||
// the process of removing hold notes can result in shortening the beatmap's play time,
|
||||
// and therefore, as a side effect, changing the most common BPM, which will change scroll speed.
|
||||
// to compensate for this, apply a multiplier to effect points in order to maintain the beatmap's original intended scroll speed.
|
||||
if (!Precision.AlmostEquals(mostCommonBeatLengthBefore, mostCommonBeatLengthAfter))
|
||||
{
|
||||
foreach (var effectPoint in beatmap.ControlPointInfo.EffectPoints)
|
||||
effectPoint.ScrollSpeed *= mostCommonBeatLengthBefore / mostCommonBeatLengthAfter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +57,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
|
||||
if (spectatorList != null)
|
||||
spectatorList.Position = new Vector2(36, -66);
|
||||
|
||||
foreach (var d in container.OfType<ISerialisableDrawable>())
|
||||
d.UsesFixedAnchor = true;
|
||||
})
|
||||
{
|
||||
new DrawableGameplayLeaderboard(),
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
|
||||
private Drawable noteAnimation = null!;
|
||||
|
||||
private float? widthForNoteHeightScale;
|
||||
private float? minimumColumnWidth;
|
||||
|
||||
public LegacyNotePiece()
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
|
||||
{
|
||||
widthForNoteHeightScale = skin.GetConfig<ManiaSkinConfigurationLookup, float>(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.WidthForNoteHeightScale))?.Value;
|
||||
minimumColumnWidth = skin.GetConfig<ManiaSkinConfigurationLookup, float>(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.MinimumColumnWidth))?.Value;
|
||||
|
||||
InternalChild = directionContainer = new Container
|
||||
{
|
||||
@@ -60,8 +60,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
|
||||
if (texture != null)
|
||||
{
|
||||
float noteHeight = widthForNoteHeightScale ?? DrawWidth;
|
||||
noteAnimation.Scale = Vector2.Divide(new Vector2(DrawWidth, noteHeight), texture.DisplayWidth);
|
||||
// The height is scaled to the minimum column width, if provided.
|
||||
float minimumWidth = minimumColumnWidth ?? DrawWidth;
|
||||
noteAnimation.Scale = Vector2.Divide(new Vector2(DrawWidth, minimumWidth), texture.DisplayWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,13 +64,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
private readonly Lazy<bool> hasKeyTexture;
|
||||
|
||||
private readonly ManiaBeatmap beatmap;
|
||||
private readonly bool isBeatmapConverted;
|
||||
|
||||
public ManiaLegacySkinTransformer(ISkin skin, IBeatmap beatmap)
|
||||
: base(skin)
|
||||
{
|
||||
this.beatmap = (ManiaBeatmap)beatmap;
|
||||
isBeatmapConverted = !beatmap.BeatmapInfo.Ruleset.Equals(new ManiaRuleset().RulesetInfo);
|
||||
|
||||
isLegacySkin = new Lazy<bool>(() => GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version) != null);
|
||||
hasKeyTexture = new Lazy<bool>(() =>
|
||||
@@ -122,9 +120,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
leaderboard.Origin = Anchor.CentreLeft;
|
||||
leaderboard.X = 10;
|
||||
}
|
||||
|
||||
foreach (var d in container.OfType<ISerialisableDrawable>())
|
||||
d.UsesFixedAnchor = true;
|
||||
})
|
||||
{
|
||||
new LegacyManiaComboCounter(),
|
||||
@@ -201,8 +196,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
|
||||
public override ISample GetSample(ISampleInfo sampleInfo)
|
||||
{
|
||||
// layered hit sounds never play in mania-native beatmaps (but do play on converts)
|
||||
if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample && legacySample.IsLayered && !isBeatmapConverted)
|
||||
// layered hit sounds never play in mania
|
||||
if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample && legacySample.IsLayered)
|
||||
return new SampleVirtual();
|
||||
|
||||
return base.GetSample(sampleInfo);
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
var hitWindows = new ManiaHitWindows();
|
||||
|
||||
AddInternal(judgementPooler = new JudgementPooler<DrawableManiaJudgement>(Enum.GetValues<HitResult>().Where(hitWindows.IsHitResultAllowed)));
|
||||
AddInternal(judgementPooler = new JudgementPooler<DrawableManiaJudgement>(Enum.GetValues<HitResult>().Where(r => hitWindows.IsHitResultAllowed(r))));
|
||||
|
||||
RegisterPool<BarLine, DrawableBarLine>(50, 200);
|
||||
}
|
||||
|
||||
@@ -35,9 +35,11 @@
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/AppIcon.appiconset</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
@@ -245,13 +245,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddAssert("grid spacing is distance to slider tail", () =>
|
||||
{
|
||||
var composer = Editor.ChildrenOfType<RectangularPositionSnapGrid>().Single();
|
||||
return Precision.AlmostEquals(composer.Spacing.Value.X, 32.05, 0.1)
|
||||
return Precision.AlmostEquals(composer.Spacing.Value.X, 32.05, 0.01)
|
||||
&& Precision.AlmostEquals(composer.Spacing.Value.X, composer.Spacing.Value.Y);
|
||||
});
|
||||
AddAssert("grid rotation points to slider tail", () =>
|
||||
{
|
||||
var composer = Editor.ChildrenOfType<RectangularPositionSnapGrid>().Single();
|
||||
return Precision.AlmostEquals(composer.GridLineRotation.Value, 0.09, 0.1);
|
||||
return Precision.AlmostEquals(composer.GridLineRotation.Value, 0.09, 0.01);
|
||||
});
|
||||
|
||||
AddStep("start grid placement", () => InputManager.Key(Key.Number5));
|
||||
@@ -280,9 +280,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddAssert("grid spacing and rotation unchanged", () =>
|
||||
{
|
||||
var composer = Editor.ChildrenOfType<RectangularPositionSnapGrid>().Single();
|
||||
return Precision.AlmostEquals(composer.Spacing.Value.X, 32.05, 0.1)
|
||||
return Precision.AlmostEquals(composer.Spacing.Value.X, 32.05, 0.01)
|
||||
&& Precision.AlmostEquals(composer.Spacing.Value.X, composer.Spacing.Value.Y)
|
||||
&& Precision.AlmostEquals(composer.GridLineRotation.Value, 0.09, 0.1);
|
||||
&& Precision.AlmostEquals(composer.GridLineRotation.Value, 0.09, 0.01);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@@ -23,12 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
[TestFixture]
|
||||
public partial class TestSceneSliderDrawing : TestSceneOsuEditor
|
||||
{
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
var beatmap = new TestBeatmap(ruleset, false);
|
||||
beatmap.ControlPointInfo.Add(0, new TimingControlPoint());
|
||||
return beatmap;
|
||||
}
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
||||
|
||||
[Test]
|
||||
public void TestTouchInputPlaceHitCircleDirectly()
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
@@ -33,16 +30,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
PathType.LINEAR,
|
||||
new Vector2(100, 0),
|
||||
new Vector2(100, 100)
|
||||
),
|
||||
createPathSegment(
|
||||
PathType.PERFECT_CURVE,
|
||||
new Vector2(100.009f, -50.0009f),
|
||||
new Vector2(200.0089f, -100)
|
||||
),
|
||||
createPathSegment(
|
||||
PathType.PERFECT_CURVE,
|
||||
new Vector2(25, -50),
|
||||
new Vector2(100, 75)
|
||||
)
|
||||
};
|
||||
|
||||
@@ -61,13 +48,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
[TestCase(0, 250)]
|
||||
[TestCase(0, 200)]
|
||||
[TestCase(1, 120, false, false)]
|
||||
[TestCase(1, 80, false, false)]
|
||||
[TestCase(2, 250)]
|
||||
[TestCase(2, 190)]
|
||||
[TestCase(3, 250)]
|
||||
[TestCase(3, 190)]
|
||||
public void TestSliderReversal(int pathIndex, double length, bool assertEqualDistances = true, bool assertSliderReduction = true)
|
||||
[TestCase(1, 120)]
|
||||
[TestCase(1, 80)]
|
||||
public void TestSliderReversal(int pathIndex, double length)
|
||||
{
|
||||
var controlPoints = paths[pathIndex];
|
||||
|
||||
@@ -107,215 +90,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
InputManager.ReleaseKey(Key.LControl);
|
||||
});
|
||||
|
||||
if (pathIndex == 2)
|
||||
{
|
||||
AddRepeatStep("Reverse slider again", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.LControl);
|
||||
InputManager.Key(Key.G);
|
||||
InputManager.ReleaseKey(Key.LControl);
|
||||
}, 2);
|
||||
}
|
||||
|
||||
if (assertEqualDistances)
|
||||
{
|
||||
AddAssert("Middle control point has the same distance from start to end", () =>
|
||||
{
|
||||
var pathControlPoints = selectedSlider.Path.ControlPoints;
|
||||
float middleToStart = Vector2.Distance(pathControlPoints[^2].Position, pathControlPoints[0].Position);
|
||||
float middleToEnd = Vector2.Distance(pathControlPoints[^2].Position, pathControlPoints[^1].Position);
|
||||
|
||||
return Precision.AlmostEquals(middleToStart, middleToEnd, 1f);
|
||||
});
|
||||
}
|
||||
|
||||
AddAssert("Middle control point is not at start or end", () =>
|
||||
Vector2.Distance(selectedSlider.Path.ControlPoints[^2].Position, oldStartPos) > 1 &&
|
||||
Vector2.Distance(selectedSlider.Path.ControlPoints[^2].Position, oldEndPos) > 1
|
||||
);
|
||||
|
||||
AddAssert("Slider has correct length", () =>
|
||||
Precision.AlmostEquals(selectedSlider.Path.Distance, oldDistance));
|
||||
|
||||
AddAssert("Slider has correct start position", () =>
|
||||
Vector2.Distance(selectedSlider.Position, oldEndPos) < 1);
|
||||
|
||||
AddAssert("Slider has correct end position", () =>
|
||||
Vector2.Distance(selectedSlider.EndPosition, oldStartPos) < 1);
|
||||
|
||||
AddAssert("Control points have correct types", () =>
|
||||
{
|
||||
var newControlPointTypes = selectedSlider.Path.ControlPoints.Select(p => p.Type).ToArray();
|
||||
|
||||
return oldControlPointTypes.Take(newControlPointTypes.Length).SequenceEqual(newControlPointTypes);
|
||||
});
|
||||
|
||||
if (assertSliderReduction)
|
||||
{
|
||||
AddStep("Move to marker", () =>
|
||||
{
|
||||
var marker = this.ChildrenOfType<SliderEndDragMarker>().Single();
|
||||
var markerPos = (marker.ScreenSpaceDrawQuad.TopRight + marker.ScreenSpaceDrawQuad.BottomRight) / 2;
|
||||
// sometimes the cursor may miss the marker's hitbox so we
|
||||
// add a little offset here to be sure it lands in a clickable position.
|
||||
var position = new Vector2(markerPos.X + 2f, markerPos.Y);
|
||||
InputManager.MoveMouseTo(position);
|
||||
});
|
||||
AddStep("Click", () => InputManager.PressButton(MouseButton.Left));
|
||||
AddStep("Reduce slider", () =>
|
||||
{
|
||||
var middleControlPoint = this.ChildrenOfType<PathControlPointPiece<Slider>>().ToArray()[^2];
|
||||
InputManager.MoveMouseTo(middleControlPoint);
|
||||
});
|
||||
AddStep("Release click", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddStep("Save half slider info", () =>
|
||||
{
|
||||
oldStartPos = selectedSlider.Position;
|
||||
oldEndPos = selectedSlider.EndPosition;
|
||||
oldDistance = selectedSlider.Path.Distance;
|
||||
});
|
||||
|
||||
AddStep("Reverse slider", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.LControl);
|
||||
InputManager.Key(Key.G);
|
||||
InputManager.ReleaseKey(Key.LControl);
|
||||
});
|
||||
|
||||
AddAssert("Middle control point has the same distance from start to end", () =>
|
||||
{
|
||||
var pathControlPoints = selectedSlider.Path.ControlPoints;
|
||||
float middleToStart = Vector2.Distance(pathControlPoints[^2].Position, pathControlPoints[0].Position);
|
||||
float middleToEnd = Vector2.Distance(pathControlPoints[^2].Position, pathControlPoints[^1].Position);
|
||||
|
||||
return Precision.AlmostEquals(middleToStart, middleToEnd, 1f);
|
||||
});
|
||||
|
||||
AddAssert("Middle control point is not at start or end", () =>
|
||||
Vector2.Distance(selectedSlider.Path.ControlPoints[^2].Position, oldStartPos) > 1 &&
|
||||
Vector2.Distance(selectedSlider.Path.ControlPoints[^2].Position, oldEndPos) > 1
|
||||
);
|
||||
|
||||
AddAssert("Slider has correct length", () =>
|
||||
Precision.AlmostEquals(selectedSlider.Path.Distance, oldDistance));
|
||||
|
||||
AddAssert("Slider has correct start position", () =>
|
||||
Vector2.Distance(selectedSlider.Position, oldEndPos) < 1);
|
||||
|
||||
AddAssert("Slider has correct end position", () =>
|
||||
Vector2.Distance(selectedSlider.EndPosition, oldStartPos) < 1);
|
||||
|
||||
AddAssert("Control points have correct types", () =>
|
||||
{
|
||||
var newControlPointTypes = selectedSlider.Path.ControlPoints.Select(p => p.Type).ToArray();
|
||||
|
||||
return oldControlPointTypes.Take(newControlPointTypes.Length).SequenceEqual(newControlPointTypes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSegmentedSliderReversal()
|
||||
{
|
||||
PathControlPoint[] segmentedSliderPath =
|
||||
[
|
||||
new PathControlPoint
|
||||
{
|
||||
Position = new Vector2(0, 0),
|
||||
Type = PathType.PERFECT_CURVE
|
||||
},
|
||||
new PathControlPoint
|
||||
{
|
||||
Position = new Vector2(100, 150),
|
||||
},
|
||||
new PathControlPoint
|
||||
{
|
||||
Position = new Vector2(75, -50),
|
||||
Type = PathType.PERFECT_CURVE
|
||||
},
|
||||
new PathControlPoint
|
||||
{
|
||||
Position = new Vector2(225, -75),
|
||||
},
|
||||
new PathControlPoint
|
||||
{
|
||||
Position = new Vector2(350, 50),
|
||||
Type = PathType.PERFECT_CURVE
|
||||
},
|
||||
new PathControlPoint
|
||||
{
|
||||
Position = new Vector2(500, -75),
|
||||
},
|
||||
new PathControlPoint
|
||||
{
|
||||
Position = new Vector2(350, -120),
|
||||
},
|
||||
];
|
||||
|
||||
Vector2 oldStartPos = default;
|
||||
Vector2 oldEndPos = default;
|
||||
double oldDistance = default;
|
||||
|
||||
var oldControlPointTypes = segmentedSliderPath.Select(p => p.Type);
|
||||
|
||||
AddStep("Add slider", () =>
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
Position = new Vector2(0, 200),
|
||||
Path = new SliderPath(segmentedSliderPath)
|
||||
{
|
||||
ExpectedDistance = { Value = 1314 }
|
||||
}
|
||||
};
|
||||
|
||||
EditorBeatmap.Add(slider);
|
||||
|
||||
oldStartPos = slider.Position;
|
||||
oldEndPos = slider.EndPosition;
|
||||
oldDistance = slider.Path.Distance;
|
||||
});
|
||||
|
||||
AddStep("Select slider", () =>
|
||||
{
|
||||
var slider = (Slider)EditorBeatmap.HitObjects[0];
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
});
|
||||
|
||||
AddRepeatStep("Reverse slider", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.LControl);
|
||||
InputManager.Key(Key.G);
|
||||
InputManager.ReleaseKey(Key.LControl);
|
||||
}, 3);
|
||||
|
||||
AddAssert("First arc's control is not at the slider's middle", () =>
|
||||
Vector2.Distance(selectedSlider.Path.ControlPoints[^2].Position, selectedSlider.Path.PositionAt(0.5)) > 1
|
||||
);
|
||||
|
||||
AddAssert("Last arc's control is not at the slider's middle", () =>
|
||||
Vector2.Distance(selectedSlider.Path.ControlPoints[1].Position, selectedSlider.Path.PositionAt(0.5)) > 1
|
||||
);
|
||||
|
||||
AddAssert("First arc centered middle control point", () =>
|
||||
{
|
||||
var pathControlPoints = selectedSlider.Path.ControlPoints;
|
||||
float middleToStart = Vector2.Distance(pathControlPoints[1].Position, pathControlPoints[0].Position);
|
||||
float middleToEnd = Vector2.Distance(pathControlPoints[1].Position, pathControlPoints[2].Position);
|
||||
|
||||
return Precision.AlmostEquals(middleToStart, middleToEnd, 1f);
|
||||
});
|
||||
|
||||
AddAssert("Last arc centered middle control point", () =>
|
||||
{
|
||||
var pathControlPoints = selectedSlider.Path.ControlPoints;
|
||||
float middleToStart = Vector2.Distance(pathControlPoints[^2].Position, pathControlPoints[^3].Position);
|
||||
float middleToEnd = Vector2.Distance(pathControlPoints[^2].Position, pathControlPoints[^1].Position);
|
||||
|
||||
return Precision.AlmostEquals(middleToStart, middleToEnd, 1f);
|
||||
});
|
||||
|
||||
AddAssert("Slider has correct length", () =>
|
||||
Precision.AlmostEquals(selectedSlider.Path.Distance, oldDistance));
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
@@ -103,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
ClassicAssert.True(Precision.AlmostEquals(sliderPathPerfect.PositionAt(i / 100.0f), sliderPathBezier.PositionAt(i / 100.0f)));
|
||||
Assert.True(Precision.AlmostEquals(sliderPathPerfect.PositionAt(i / 100.0f), sliderPathBezier.PositionAt(i / 100.0f)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
double theta = circularArcProperties.ThetaStart + (circularArcProperties.Direction * progress * circularArcProperties.ThetaRange);
|
||||
Vector2 vector = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * circularArcProperties.Radius;
|
||||
|
||||
ClassicAssert.True(Precision.AlmostEquals(circularArcProperties.Centre + vector, path.PositionAt(progress), 0.01f),
|
||||
Assert.True(Precision.AlmostEquals(circularArcProperties.Centre + vector, path.PositionAt(progress), 0.01f),
|
||||
"A perfect circle with points " + string.Join(", ", path.ControlPoints.Select(x => x.Position)) + " and radius" + circularArcProperties.Radius + "from SliderPath does not almost equal a theoretical perfect circle with " + subpoints + " subpoints"
|
||||
+ ": " + (circularArcProperties.Centre + vector) + " - " + path.PositionAt(progress)
|
||||
+ " = " + (circularArcProperties.Centre + vector - path.PositionAt(progress))
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
public partial class TestSceneOsuModEasy : OsuModTestScene
|
||||
{
|
||||
protected override bool AllowFail => true;
|
||||
|
||||
[Test]
|
||||
public void TestMultipleApplication()
|
||||
{
|
||||
bool reapplied = false;
|
||||
CreateModTest(new ModTestData
|
||||
{
|
||||
Mods = [new OsuModEasy { Retries = { Value = 1 } }],
|
||||
Autoplay = false,
|
||||
CreateBeatmap = () =>
|
||||
{
|
||||
// do stuff to speed up fails
|
||||
var b = new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
Difficulty = { DrainRate = 10 }
|
||||
};
|
||||
|
||||
foreach (var ho in b.HitObjects)
|
||||
ho.StartTime /= 4;
|
||||
|
||||
return b;
|
||||
},
|
||||
PassCondition = () =>
|
||||
{
|
||||
if (((ModEasyTestPlayer)Player).FailuresSuppressed > 0 && !reapplied)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var mod in Player.GameplayState.Mods.OfType<IApplicableToDifficulty>())
|
||||
mod.ApplyToDifficulty(new BeatmapDifficulty());
|
||||
|
||||
foreach (var mod in Player.GameplayState.Mods.OfType<IApplicableToPlayer>())
|
||||
mod.ApplyToPlayer(Player);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// don't care if this fails. in fact a failure here is probably better than the alternative.
|
||||
}
|
||||
finally
|
||||
{
|
||||
reapplied = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Player.GameplayState.HasFailed && ((ModEasyTestPlayer)Player).FailuresSuppressed <= 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override TestPlayer CreateModPlayer(Ruleset ruleset) => new ModEasyTestPlayer(CurrentTestData, AllowFail);
|
||||
|
||||
private partial class ModEasyTestPlayer : ModTestPlayer
|
||||
{
|
||||
public int FailuresSuppressed { get; private set; }
|
||||
|
||||
public ModEasyTestPlayer(ModTestData data, bool allowFail)
|
||||
: base(data, allowFail)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CheckModsAllowFailure()
|
||||
{
|
||||
bool failureAllowed = GameplayState.Mods.OfType<IApplicableFailOverride>().All(m => m.PerformFail());
|
||||
|
||||
if (!failureAllowed)
|
||||
FailuresSuppressed++;
|
||||
|
||||
return failureAllowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
@@ -21,39 +18,5 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
Autoplay = false,
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSkipToFirstCircleNotSuppressed()
|
||||
{
|
||||
CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModFreezeFrame(),
|
||||
CreateBeatmap = () => new OsuBeatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 5000, Position = OsuPlayfield.BASE_SIZE / 2 }
|
||||
}
|
||||
},
|
||||
PassCondition = () => Player.GameplayClockContainer.GameplayStartTime > 0
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSkipToFirstSpinnerNotSuppressed()
|
||||
{
|
||||
CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModFreezeFrame(),
|
||||
CreateBeatmap = () => new OsuBeatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new Spinner { StartTime = 5000, Position = OsuPlayfield.BASE_SIZE / 2 }
|
||||
}
|
||||
},
|
||||
PassCondition = () => Player.GameplayClockContainer.GameplayStartTime > 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase("multi-segment-slider")]
|
||||
[TestCase("nan-slider")]
|
||||
[TestCase("1124896")]
|
||||
[TestCase("1341554")]
|
||||
[TestCase("2593923")]
|
||||
[TestCase("801165")]
|
||||
public void Test(string name) => base.Test(name);
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
|
||||
@@ -15,49 +15,25 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu.Tests";
|
||||
|
||||
[TestCase(6.6232533278125061d, 239, "diffcalc-test")]
|
||||
[TestCase(1.5045783545699611d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.43333836671191595d, 4, "very-fast-slider")]
|
||||
[TestCase(0.13841532030395723d, 2, "nan-slider")]
|
||||
[TestCase(6.7331304290522747d, 239, "diffcalc-test")]
|
||||
[TestCase(1.4602604078137214d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.43052813047866129d, 4, "very-fast-slider")]
|
||||
[TestCase(0.14143808967817237d, 2, "nan-slider")]
|
||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||
|
||||
[TestCase(9.6491691624112761d, 239, "diffcalc-test")]
|
||||
[TestCase(1.756936832498702d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.57771197086735004d, 4, "very-fast-slider")]
|
||||
[TestCase(9.6779746353001634d, 239, "diffcalc-test")]
|
||||
[TestCase(1.7691451263718989d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.55785578988249407d, 4, "very-fast-slider")]
|
||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
||||
|
||||
[TestCase(6.6232533278125061d, 239, "diffcalc-test")]
|
||||
[TestCase(1.5045783545699611d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.43333836671191595d, 4, "very-fast-slider")]
|
||||
[TestCase(6.7331304290522747d, 239, "diffcalc-test")]
|
||||
[TestCase(1.4602604078137214d, 54, "zero-length-sliders")]
|
||||
[TestCase(0.43052813047866129d, 4, "very-fast-slider")]
|
||||
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
||||
|
||||
[TestCase(239, "diffcalc-test")]
|
||||
[TestCase(54, "zero-length-sliders")]
|
||||
[TestCase(4, "very-fast-slider")]
|
||||
public void TestOffsetChanges(int expectedMaxCombo, string name)
|
||||
{
|
||||
const double offset_iterations = 400;
|
||||
var beatmap = GetBeatmap(name);
|
||||
|
||||
var attributes = CreateDifficultyCalculator(beatmap).Calculate();
|
||||
double expectedStarRating = attributes.StarRating;
|
||||
|
||||
for (int i = 0; i < offset_iterations; i++)
|
||||
{
|
||||
foreach (var beatmapHitObject in beatmap.Beatmap.HitObjects)
|
||||
beatmapHitObject.StartTime++;
|
||||
|
||||
attributes = CreateDifficultyCalculator(beatmap).Calculate();
|
||||
|
||||
// Platform-dependent math functions (Pow, Cbrt, Exp, etc) may result in minute differences.
|
||||
Assert.That(attributes.StarRating, Is.EqualTo(expectedStarRating).Within(0.00001));
|
||||
Assert.That(attributes.MaxCombo, Is.EqualTo(expectedMaxCombo));
|
||||
}
|
||||
}
|
||||
|
||||
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap);
|
||||
|
||||
protected override Ruleset CreateRuleset() => new OsuRuleset();
|
||||
|
||||
-1
File diff suppressed because one or more lines are too long
@@ -1,941 +0,0 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
AudioLeadIn: 0
|
||||
PreviewTime: 76429
|
||||
Countdown: 0
|
||||
SampleSet: Soft
|
||||
StackLeniency: 0.2
|
||||
Mode: 0
|
||||
LetterboxInBreaks: 0
|
||||
WidescreenStoryboard: 1
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:5
|
||||
CircleSize:4.3
|
||||
OverallDifficulty:8
|
||||
ApproachRate:9.3
|
||||
SliderMultiplier:2.99999995231628
|
||||
SliderTickRate:1
|
||||
|
||||
[Events]
|
||||
//Background and Video events
|
||||
//Break Periods
|
||||
//Storyboard Layer 0 (Background)
|
||||
//Storyboard Layer 1 (Fail)
|
||||
//Storyboard Layer 2 (Pass)
|
||||
//Storyboard Layer 3 (Foreground)
|
||||
//Storyboard Sound Samples
|
||||
|
||||
[TimingPoints]
|
||||
763,444.444444444444,4,2,1,60,1,0
|
||||
763,-111.111111111111,4,2,1,60,0,0
|
||||
1929,-100,4,2,1,5,0,0
|
||||
1985,-100,4,2,1,60,0,0
|
||||
2040,-100,4,2,1,5,0,0
|
||||
2096,-153.846153846153,4,2,1,60,0,0
|
||||
2429,-133.333333333333,4,2,1,5,0,0
|
||||
2540,-71.4285714285714,4,2,1,70,0,1
|
||||
2985,-100,4,2,1,70,0,1
|
||||
4485,-100,4,2,1,5,0,1
|
||||
4540,-100,4,2,1,70,0,1
|
||||
4707,-100,4,2,1,5,0,1
|
||||
4762,-100,4,2,1,70,0,1
|
||||
4929,-100,4,2,1,5,0,1
|
||||
4985,-100,4,2,1,70,0,1
|
||||
5096,-83.3333333333333,4,2,1,70,0,1
|
||||
5429,-133.333333333333,4,2,1,70,0,1
|
||||
5596,-133.333333333333,4,2,1,70,0,1
|
||||
5652,-133.333333333333,4,2,1,70,0,1
|
||||
5818,-133.333333333333,4,2,1,70,0,1
|
||||
5874,-133.333333333333,4,2,1,70,0,1
|
||||
6040,-133.333333333333,4,2,1,70,0,1
|
||||
6096,-100,4,2,1,70,0,1
|
||||
6540,-100,4,2,1,70,0,1
|
||||
8040,-100,4,2,1,5,0,1
|
||||
8096,-100,4,2,1,70,0,1
|
||||
8262,-100,4,2,1,5,0,1
|
||||
8318,-100,4,2,1,70,0,1
|
||||
8485,-100,4,2,1,5,0,1
|
||||
8540,-133.333333333333,4,2,1,70,0,1
|
||||
8874,-100,4,2,1,5,0,1
|
||||
8985,-100,4,2,1,70,0,1
|
||||
9651,-100,4,2,1,70,0,1
|
||||
10096,-100,4,2,1,70,0,1
|
||||
11596,-100,4,2,1,5,0,1
|
||||
11651,-100,4,2,1,70,0,1
|
||||
11818,-100,4,2,1,5,0,1
|
||||
11873,-100,4,2,1,70,0,1
|
||||
11874,-80,4,2,1,70,0,1
|
||||
12040,-80,4,2,1,5,0,1
|
||||
12096,-80,4,2,1,70,0,1
|
||||
12207,-133.333333333333,4,2,1,70,0,1
|
||||
12429,-100,4,2,1,5,0,1
|
||||
12540,-100,4,2,1,70,0,1
|
||||
12707,-100,4,2,1,70,0,1
|
||||
12763,-100,4,2,1,70,0,1
|
||||
12929,-100,4,2,1,70,0,1
|
||||
12985,-100,4,2,1,70,0,1
|
||||
13429,-300,4,2,1,70,0,1
|
||||
13651,-83.3333333333333,4,2,1,70,0,1
|
||||
13874,-100,4,2,1,70,0,1
|
||||
15151,-100,4,2,1,5,0,1
|
||||
15207,-100,4,2,1,70,0,1
|
||||
15373,-100,4,2,1,5,0,1
|
||||
15429,-100,4,2,1,70,0,1
|
||||
15596,-100,4,2,1,5,0,1
|
||||
15651,-100,4,2,1,70,0,1
|
||||
15985,-100,4,2,1,5,0,1
|
||||
16096,-100,4,2,1,70,0,1
|
||||
16262,-100,4,2,1,70,0,1
|
||||
16318,-83.3333333333333,4,2,1,70,0,1
|
||||
16651,-100,4,2,1,70,0,1
|
||||
16762,-133.333333333333,4,2,1,60,0,0
|
||||
17096,-133.333333333333,4,2,1,5,0,0
|
||||
17207,-200,4,2,1,60,0,0
|
||||
18096,-66.6666666666667,4,2,1,60,0,0
|
||||
18262,-66.6666666666667,4,2,1,5,0,0
|
||||
18318,-66.6666666666667,4,2,1,60,0,0
|
||||
18540,-100,4,2,1,60,0,0
|
||||
18874,-100,4,2,1,5,0,0
|
||||
18985,-100,4,2,1,60,0,0
|
||||
19985,-100,4,2,1,60,0,0
|
||||
20485,-100,4,2,1,5,0,0
|
||||
20540,-100,4,2,1,60,0,0
|
||||
20707,-100,4,2,1,5,0,0
|
||||
20762,-200,4,2,1,60,0,0
|
||||
20985,-100,4,2,1,60,0,0
|
||||
21095,-100,4,2,1,60,0,0
|
||||
21374,-100,4,2,1,5,0,0
|
||||
21429,-100,4,2,1,60,0,0
|
||||
21596,-100,4,2,1,5,0,0
|
||||
21651,-100,4,2,1,60,0,0
|
||||
21818,-100,4,2,1,5,0,0
|
||||
21874,-66.6666666666667,4,2,1,60,0,0
|
||||
21985,-100,4,2,1,60,0,0
|
||||
22096,-100,4,2,1,60,0,0
|
||||
22985,-200,4,2,1,60,0,0
|
||||
23318,-100,4,2,1,60,0,0
|
||||
23429,-100,4,2,1,60,0,0
|
||||
23540,-100,4,2,1,60,0,0
|
||||
23651,-100,4,2,1,60,0,0
|
||||
23762,-100,4,2,1,60,0,0
|
||||
23874,-133.333333333333,4,2,1,60,0,0
|
||||
24208,-133.333333333333,4,2,1,5,0,0
|
||||
24318,-200,4,2,1,5,0,0
|
||||
24319,-200,4,2,1,60,0,0
|
||||
24540,-100,4,2,1,60,0,0
|
||||
24651,-66.6666666666667,4,2,1,60,0,0
|
||||
24874,-100,4,2,1,60,0,0
|
||||
25374,-100,4,2,1,5,0,0
|
||||
25429,-100,4,2,1,60,0,0
|
||||
27096,-100,4,2,1,60,0,0
|
||||
27596,-100,4,2,1,5,0,0
|
||||
27651,-100,4,2,1,60,0,0
|
||||
27818,-100,4,2,1,5,0,0
|
||||
27873,-133.333333333333,4,2,1,60,0,0
|
||||
28096,-100,4,2,1,60,0,0
|
||||
28206,-100,4,2,1,60,0,0
|
||||
28485,-100,4,2,1,5,0,0
|
||||
28540,-100,4,2,1,60,0,0
|
||||
28707,-100,4,2,1,5,0,0
|
||||
28762,-100,4,2,1,60,0,0
|
||||
28929,-100,4,2,1,5,0,0
|
||||
28985,-66.6666666666667,4,2,1,60,0,0
|
||||
29151,-100,4,2,1,60,0,0
|
||||
29207,-100,4,2,1,60,0,0
|
||||
29651,-100,4,2,1,60,0,0
|
||||
30429,-100,4,2,1,60,0,0
|
||||
30540,-58.8235294117647,4,2,1,60,0,0
|
||||
30874,-58.8235294117647,4,2,1,5,0,0
|
||||
30985,-58.8235294117647,4,2,1,60,0,0
|
||||
31040,-100,4,2,1,5,0,0
|
||||
31429,-100,4,2,1,60,0,0
|
||||
32485,-100,4,2,1,60,0,0
|
||||
32540,-100,4,2,1,60,0,0
|
||||
32707,-100,4,2,1,60,0,0
|
||||
32762,-100,4,2,1,60,0,0
|
||||
32985,-100,4,2,1,60,0,0
|
||||
34318,-50,4,2,1,60,0,0
|
||||
34485,-100,4,2,1,5,0,0
|
||||
34540,-100,4,2,1,60,0,0
|
||||
35151,-100,4,2,1,5,0,0
|
||||
35207,-100,4,2,1,60,0,0
|
||||
35374,-100,4,2,1,5,0,0
|
||||
35430,-100,4,2,1,60,0,0
|
||||
35818,-100,4,2,1,5,0,0
|
||||
35874,-200,4,2,1,60,0,0
|
||||
36429,-100,4,2,1,60,0,0
|
||||
37818,-100,4,2,1,5,0,0
|
||||
37874,-100,4,2,1,60,0,0
|
||||
38040,-100,4,2,1,5,0,0
|
||||
38096,-50,4,2,1,60,0,0
|
||||
38151,-100,4,2,1,5,0,0
|
||||
38540,-100,4,2,1,60,0,0
|
||||
39596,-100,4,2,1,5,0,0
|
||||
39651,-100,4,2,1,60,0,0
|
||||
39818,-100,4,2,1,60,0,0
|
||||
39873,-100,4,2,1,60,0,0
|
||||
40096,-100,4,2,1,60,0,0
|
||||
41429,-50,4,2,1,60,0,0
|
||||
41596,-100,4,2,1,5,0,0
|
||||
41651,-100,4,2,1,60,0,0
|
||||
41818,-100,4,2,1,5,0,0
|
||||
41874,-100,4,2,1,60,0,0
|
||||
42040,-100,4,2,1,5,0,0
|
||||
42096,-100,4,2,1,60,0,0
|
||||
44318,-100,4,2,1,60,0,0
|
||||
44762,-83.3333333333333,4,2,1,60,0,0
|
||||
45207,-66.6666666666667,4,2,1,45,0,0
|
||||
45651,-133.333333333333,4,2,1,45,0,0
|
||||
51540,-133.333333333333,4,2,1,50,0,0
|
||||
51651,-133.333333333333,4,2,1,45,0,0
|
||||
52318,-133.333333333333,4,2,1,45,0,0
|
||||
58540,-76.9230769230769,4,2,1,45,0,0
|
||||
58818,-100,4,2,1,45,0,0
|
||||
58874,-111.111111111111,4,2,1,45,0,0
|
||||
59318,-111.111111111111,4,2,1,45,0,0
|
||||
59429,-83.3333333333333,4,2,1,60,0,0
|
||||
59540,-83.3333333333333,4,2,1,5,0,0
|
||||
59874,-100,4,2,1,60,0,0
|
||||
60096,-100,4,2,1,5,0,0
|
||||
60207,-100,4,2,1,60,0,0
|
||||
60707,-100,4,2,1,5,0,0
|
||||
60763,-100,4,2,1,60,0,0
|
||||
60818,-100,4,2,1,5,0,0
|
||||
60874,-100,4,2,1,60,0,0
|
||||
60929,-100,4,2,1,5,0,0
|
||||
60985,-100,4,2,1,60,0,0
|
||||
61040,-100,4,2,1,5,0,0
|
||||
61096,-100,4,2,1,60,0,0
|
||||
61151,-100,4,2,1,5,0,0
|
||||
61207,-100,4,2,1,60,0,0
|
||||
61596,-100,4,2,1,5,0,0
|
||||
61651,-100,4,2,1,60,0,0
|
||||
61762,-83.3333333333333,4,2,1,60,0,0
|
||||
61985,-100,4,2,1,5,0,0
|
||||
62096,-100,4,2,1,60,0,0
|
||||
62151,-100,4,2,1,5,0,0
|
||||
62207,-100,4,2,1,60,0,0
|
||||
62262,-100,4,2,1,5,0,0
|
||||
62318,-100,4,2,1,60,0,0
|
||||
62374,-100,4,2,1,5,0,0
|
||||
62430,-100,4,2,1,60,0,0
|
||||
62485,-100,4,2,1,5,0,0
|
||||
62540,-100,4,2,1,60,0,0
|
||||
62596,-100,4,2,1,5,0,0
|
||||
62651,-100,4,2,1,60,0,0
|
||||
62707,-100,4,2,1,5,0,0
|
||||
62762,-100,4,2,1,60,0,0
|
||||
62818,-100,4,2,1,5,0,0
|
||||
62874,-100,4,2,1,60,0,0
|
||||
62929,-100,4,2,1,60,0,0
|
||||
62930,-100,4,2,1,5,0,0
|
||||
62985,-100,4,2,1,60,0,0
|
||||
63707,-100,4,2,1,5,0,0
|
||||
63762,-100,4,2,1,60,0,0
|
||||
64262,-100,4,2,1,5,0,0
|
||||
64318,-100,4,2,1,60,0,0
|
||||
64485,-100,4,2,1,5,0,0
|
||||
64540,-100,4,2,1,60,0,0
|
||||
64596,-100,4,2,1,5,0,0
|
||||
64651,-100,4,2,1,60,0,0
|
||||
64707,-100,4,2,1,5,0,0
|
||||
64762,-71.4285714285714,4,2,1,60,0,0
|
||||
64929,-71.4285714285714,4,2,1,5,0,0
|
||||
64984,-133.333333333333,4,2,1,60,0,0
|
||||
65151,-133.333333333333,4,2,1,5,0,0
|
||||
65206,-71.4285714285714,4,2,1,60,0,0
|
||||
65374,-71.4285714285714,4,2,1,5,0,0
|
||||
65429,-133.333333333333,4,2,1,60,0,0
|
||||
65596,-133.333333333333,4,2,1,5,0,0
|
||||
65651,-100,4,2,1,60,0,0
|
||||
66540,-66.6666666666667,4,2,1,60,0,0
|
||||
66596,-66.6666666666667,4,2,1,5,0,0
|
||||
66929,-100,4,2,1,5,0,0
|
||||
66985,-200,4,2,1,60,0,0
|
||||
67207,-200,4,2,1,5,0,0
|
||||
67318,-100,4,2,1,60,0,0
|
||||
67818,-100,4,2,1,5,0,0
|
||||
67874,-100,4,2,1,60,0,0
|
||||
67929,-100,4,2,1,5,0,0
|
||||
67985,-100,4,2,1,60,0,0
|
||||
68040,-100,4,2,1,5,0,0
|
||||
68096,-100,4,2,1,60,0,0
|
||||
68151,-100,4,2,1,5,0,0
|
||||
68207,-100,4,2,1,60,0,0
|
||||
68262,-100,4,2,1,5,0,0
|
||||
68318,-100,4,2,1,60,0,0
|
||||
68874,-83.3333333333333,4,2,1,60,0,0
|
||||
69096,-100,4,2,1,60,0,0
|
||||
69097,-100,4,2,1,5,0,0
|
||||
69207,-100,4,2,1,60,0,0
|
||||
69263,-100,4,2,1,5,0,0
|
||||
69319,-100,4,2,1,60,0,0
|
||||
69374,-100,4,2,1,5,0,0
|
||||
69430,-100,4,2,1,60,0,0
|
||||
69486,-100,4,2,1,5,0,0
|
||||
69542,-100,4,2,1,60,0,0
|
||||
69597,-100,4,2,1,5,0,0
|
||||
69651,-100,4,2,1,60,0,0
|
||||
69707,-100,4,2,1,5,0,0
|
||||
69762,-100,4,2,1,60,0,0
|
||||
69818,-100,4,2,1,5,0,0
|
||||
69874,-100,4,2,1,60,0,0
|
||||
69929,-100,4,2,1,5,0,0
|
||||
69985,-100,4,2,1,60,0,0
|
||||
70040,-100,4,2,1,60,0,0
|
||||
70041,-100,4,2,1,5,0,0
|
||||
70096,-100,4,2,1,60,0,0
|
||||
70818,-100,4,2,1,5,0,0
|
||||
70873,-100,4,2,1,60,0,0
|
||||
71207,-71.4285714285714,4,2,1,60,0,0
|
||||
71429,-100,4,2,1,60,0,0
|
||||
71874,-71.4285714285714,4,2,1,60,0,0
|
||||
72041,-71.4285714285714,4,2,1,5,0,0
|
||||
72096,-133.333333333333,4,2,1,60,0,0
|
||||
72263,-133.333333333333,4,2,1,5,0,0
|
||||
72318,-71.4285714285714,4,2,1,60,0,0
|
||||
72485,-71.4285714285714,4,2,1,5,0,0
|
||||
72540,-133.333333333333,4,2,1,60,0,0
|
||||
72985,-66.6666666666667,4,2,1,60,0,0
|
||||
73207,-100,4,2,1,60,0,0
|
||||
73651,-133.333333333333,4,2,1,45,0,0
|
||||
75318,-133.333333333333,4,2,1,5,0,0
|
||||
75429,-133.333333333333,4,2,1,45,0,0
|
||||
76762,-100,4,2,1,45,0,0
|
||||
77096,-100,4,2,1,5,0,0
|
||||
77207,-100,4,2,1,70,0,1
|
||||
77818,-100,4,2,1,5,0,1
|
||||
77874,-100,4,2,1,70,0,1
|
||||
78262,-100,4,2,1,5,0,1
|
||||
78318,-100,4,2,1,70,0,1
|
||||
78540,-83.3333333333333,4,2,1,70,0,1
|
||||
78985,-100,4,2,1,70,0,1
|
||||
79596,-100,4,2,1,5,0,1
|
||||
79651,-100,4,2,1,70,0,1
|
||||
80040,-100,4,2,1,5,0,1
|
||||
80096,-100,4,2,1,70,0,1
|
||||
80318,-83.3333333333333,4,2,1,70,0,1
|
||||
84318,-100,4,2,1,70,0,1
|
||||
84929,-100,4,2,1,5,0,1
|
||||
84985,-100,4,2,1,70,0,1
|
||||
85207,-100,4,2,1,70,0,1
|
||||
85374,-100,4,2,1,5,0,1
|
||||
85429,-100,4,2,1,70,0,1
|
||||
85651,-83.3333333333333,4,2,1,70,0,1
|
||||
86096,-100,4,2,1,70,0,1
|
||||
86707,-100,4,2,1,5,0,1
|
||||
86762,-100,4,2,1,70,0,1
|
||||
88818,-100,4,2,1,5,0,1
|
||||
88874,-100,4,2,1,70,0,1
|
||||
88929,-100,4,2,1,5,0,1
|
||||
88985,-100,4,2,1,70,0,1
|
||||
89040,-100,4,2,1,5,0,1
|
||||
89096,-100,4,2,1,70,0,1
|
||||
92040,-100,4,2,1,5,0,1
|
||||
92096,-100,4,2,1,70,0,1
|
||||
92485,-100,4,2,1,5,0,1
|
||||
92540,-100,4,2,1,70,0,1
|
||||
97651,-200,4,2,1,70,0,1
|
||||
97818,-200,4,2,1,5,0,1
|
||||
97874,-66.6666666666667,4,2,1,70,0,1
|
||||
97985,-66.6666666666667,4,2,1,70,0,1
|
||||
98040,-66.6666666666667,4,2,1,5,0,1
|
||||
98096,-133.333333333333,4,2,1,70,0,1
|
||||
98262,-133.333333333333,4,2,1,5,0,1
|
||||
98318,-66.6666666666667,4,2,1,70,0,1
|
||||
98540,-100,4,2,1,70,0,1
|
||||
99151,-100,4,2,1,5,0,1
|
||||
99207,-100,4,2,1,70,0,1
|
||||
99596,-100,4,2,1,5,0,1
|
||||
99651,-100,4,2,1,70,0,1
|
||||
103040,-100,4,2,1,5,0,1
|
||||
103096,-100,4,2,1,70,0,1
|
||||
103151,-100,4,2,1,5,0,1
|
||||
103207,-100,4,2,1,70,0,1
|
||||
103262,-100,4,2,1,5,0,1
|
||||
103318,-100,4,2,1,70,0,1
|
||||
105207,-83.3333333333333,4,2,1,70,0,1
|
||||
105540,-83.3333333333333,4,2,1,70,0,1
|
||||
105651,-133.333333333333,4,2,1,60,0,0
|
||||
105985,-133.333333333333,4,2,1,5,0,0
|
||||
106096,-200,4,2,1,60,0,0
|
||||
106985,-66.6666666666667,4,2,1,60,0,0
|
||||
107151,-66.6666666666667,4,2,1,5,0,0
|
||||
107207,-66.6666666666667,4,2,1,60,0,0
|
||||
107429,-100,4,2,1,60,0,0
|
||||
107763,-100,4,2,1,5,0,0
|
||||
107874,-100,4,2,1,60,0,0
|
||||
108874,-100,4,2,1,60,0,0
|
||||
109374,-100,4,2,1,5,0,0
|
||||
109429,-100,4,2,1,60,0,0
|
||||
109596,-100,4,2,1,5,0,0
|
||||
109651,-200,4,2,1,60,0,0
|
||||
109929,-100,4,2,1,60,0,0
|
||||
109984,-100,4,2,1,60,0,0
|
||||
110262,-100,4,2,1,5,0,0
|
||||
110318,-100,4,2,1,60,0,0
|
||||
110485,-100,4,2,1,5,0,0
|
||||
110540,-100,4,2,1,60,0,0
|
||||
110707,-100,4,2,1,5,0,0
|
||||
110762,-66.6666666666667,4,2,1,60,0,0
|
||||
110929,-100,4,2,1,60,0,0
|
||||
110985,-133.333333333333,4,2,1,60,0,0
|
||||
111429,-133.333333333333,4,2,1,60,0,0
|
||||
111596,-133.333333333333,4,2,1,60,0,0
|
||||
111651,-133.333333333333,4,2,1,60,0,0
|
||||
111818,-133.333333333333,4,2,1,60,0,0
|
||||
111874,-100,4,2,1,60,0,1
|
||||
112318,-83.3333333333333,4,2,1,60,0,1
|
||||
112429,-100,4,2,1,5,0,0
|
||||
|
||||
|
||||
[Colours]
|
||||
Combo1 : 112,75,180
|
||||
Combo2 : 0,255,255
|
||||
Combo3 : 255,15,117
|
||||
Combo4 : 255,135,15
|
||||
|
||||
[HitObjects]
|
||||
309,230,763,37,0,3:0:0:0:
|
||||
485,146,985,2,0,L|406:167,1,67.4999968671799,8|0,3:0|0:0,0:0:0:0:
|
||||
374,249,1207,2,0,L|299:227,1,67.4999968671799,8|0,3:0|0:0,0:0:0:0:
|
||||
196,91,1429,2,0,L|191:44,3,33.7499984335899,0|0|0|0,3:0|3:0|3:0|3:0,0:0:0:0:
|
||||
124,173,1651,2,0,L|131:222,2,44.9999979114532,0|0|0,3:0|3:0|3:0,0:0:0:0:
|
||||
221,284,1874,2,0,L|213:208,1,67.4999968671799,0|0,3:0|3:0,0:0:0:0:
|
||||
292,86,2096,38,0,L|310:234,1,146.249990980625,12|0,3:0|0:0,0:0:0:0:
|
||||
314,328,2540,38,0,B|280:359|280:359|230:320|252:242|313:230,1,209.999990253448,0|0,3:0|0:0,0:0:0:0:
|
||||
421,300,2874,1,0,0:0:0:0:
|
||||
421,300,2985,2,0,P|461:288|491:253,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
309,231,3207,2,0,P|297:190|305:153,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
394,22,3429,5,0,3:0:0:0:
|
||||
461,72,3540,2,0,B|477:103|477:103|461:148,1,74.999998807907,0|4,0:0|0:0,0:0:0:0:
|
||||
378,183,3762,2,0,L|206:157,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
229,161,4096,2,0,P|227:202|211:250,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
61,384,4318,38,0,P|101:359|134:322,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
317,310,4540,2,0,P|267:305|226:288,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
141,110,4762,2,0,B|121:175|152:226|152:226|152:202|161:183,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
155,196,5096,6,0,P|67:211|79:286,1,179.999991645813,0|0,0:0|0:0,0:0:0:0:
|
||||
212,366,5429,38,0,P|207:335|174:281,1,56.2500012516975,4|0,0:0|0:0,0:0:0:0:
|
||||
206,286,5651,2,0,P|236:297|299:295,1,56.2500012516975,8|0,3:0|0:0,0:0:0:0:
|
||||
281,321,5874,2,0,P|257:340|227:396,1,56.2500012516975,4|0,0:0|0:0,0:0:0:0:
|
||||
124,246,6096,6,0,P|198:198|277:232,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
253,211,6429,1,0,0:0:0:0:
|
||||
276,99,6540,2,0,P|335:139|369:215,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
368,208,6874,1,0,0:0:0:0:
|
||||
430,96,6985,37,0,3:0:0:0:
|
||||
497,147,7096,2,0,P|507:189|488:244,1,74.999998807907,0|4,0:0|0:0,0:0:0:0:
|
||||
414,379,7318,2,0,B|383:322|421:267|421:267|421:308,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
421,298,7651,2,0,P|378:312|336:304,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
270,170,7874,6,0,P|275:228|236:278,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
94,300,8096,2,0,P|133:263|208:274,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
261,374,8318,2,0,L|176:365,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
38,377,8540,2,0,L|55:197,1,168.750003755093,4|0,0:0|0:0,0:0:0:0:
|
||||
123,25,8985,38,0,L|132:110,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
217,242,9207,2,0,L|237:168,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
48,92,9429,5,4,0:0:0:0:
|
||||
63,176,9540,1,0,0:0:0:0:
|
||||
83,259,9651,38,0,P|167:223|231:255,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
274,312,9985,1,0,0:0:0:0:
|
||||
274,312,10096,2,0,L|354:292,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
459,225,10318,2,0,L|375:204,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
269,107,10540,1,0,3:0:0:0:
|
||||
276,54,10651,1,0,0:0:0:0:
|
||||
313,17,10762,1,4,0:0:0:0:
|
||||
363,9,10874,1,0,0:0:0:0:
|
||||
363,9,11096,5,0,0:0:0:0:
|
||||
432,68,11207,2,0,P|444:107|425:154,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
309,252,11429,38,0,P|297:195|321:158,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
450,316,11651,2,0,L|361:312,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
160,341,11874,2,0,B|187:380|187:380|233:309|177:235,1,187.499997019767,8|4,3:0|0:0,0:0:0:0:
|
||||
116,200,12207,6,0,P|52:224|122:264,1,168.750003755093,0|4,0:0|0:0,0:0:0:0:
|
||||
297,91,12762,37,8,3:0:0:0:
|
||||
276,44,12874,1,0,0:0:0:0:
|
||||
226,27,12985,1,4,0:0:0:0:
|
||||
187,63,13096,1,0,0:0:0:0:
|
||||
196,115,13207,1,0,0:0:0:0:
|
||||
376,144,13429,2,0,L|378:121,2,16.6666664017571,0|0|0,0:0|0:0|0:0,0:0:0:0:
|
||||
436,220,13651,6,0,B|395:211|373:164|373:164|332:208|264:185,1,179.999991645813,8|4,3:0|0:0,0:0:0:0:
|
||||
276,44,13985,1,0,0:0:0:0:
|
||||
196,115,14096,38,0,L|139:124,4,37.4999994039535,0|0|0|0|4,3:0|0:0|0:0|0:0|0:0,0:0:0:0:
|
||||
82,69,14429,1,0,0:0:0:0:
|
||||
106,190,14540,2,0,L|126:276,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
218,383,14762,2,0,L|234:309,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
26,231,14985,5,0,3:0:0:0:
|
||||
253,202,15207,37,0,0:0:0:0:
|
||||
331,271,15318,1,0,0:0:0:0:
|
||||
233,309,15429,1,8,3:0:0:0:
|
||||
389,73,15651,6,0,P|410:22|447:112,1,224.999996423721,4|0,3:0|0:0,0:0:0:0:
|
||||
391,165,16096,1,0,0:0:0:0:
|
||||
377,177,16207,1,0,0:0:0:0:
|
||||
365,187,16318,38,0,B|253:261|221:119|94:192,1,269.999987468719,0|0,0:0|0:0,0:0:0:0:
|
||||
73,319,16762,22,0,P|133:336|116:236,1,168.750003755093,4|0,3:0|0:0,0:0:0:0:
|
||||
139,258,17207,6,0,P|138:315|69:283,1,112.500002503395,8|0,3:0|0:0,0:0:0:0:
|
||||
92,323,17762,37,0,0:0:0:0:
|
||||
43,245,17874,1,4,0:0:0:0:
|
||||
4,322,17985,1,0,0:0:0:0:
|
||||
133,245,18096,1,0,3:0:0:0:
|
||||
29,105,18318,6,0,L|38:40,3,56.2500012516975,4|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
50,30,18540,38,0,P|111:56|193:25,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
240,120,18985,2,0,P|328:91|394:125,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
409,213,19318,2,0,B|377:226|377:226|243:200,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
119,187,19651,2,0,L|127:286,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
179,338,19874,1,8,3:0:0:0:
|
||||
45,307,19985,6,0,L|3:297,2,37.4999994039535,0|0|4,0:0|0:0|0:0,0:0:0:0:
|
||||
103,380,20207,1,0,3:0:0:0:
|
||||
212,257,20318,38,0,P|233:218|231:171,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
111,118,20540,1,4,0:0:0:0:
|
||||
111,118,20762,6,0,L|197:109,1,74.999998807907,8|4,3:0|0:0,0:0:0:0:
|
||||
256,18,21096,37,0,0:0:0:0:
|
||||
337,121,21207,2,0,P|350:60|403:16,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
384,26,21429,2,0,P|406:86|465:122,1,112.49999821186,0|0,0:0|0:0,0:0:0:0:
|
||||
443,114,21651,2,0,P|377:105|327:131,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
352,223,21874,6,0,B|369:230|369:230|391:228|391:228|416:239|416:239|440:235|440:235|462:244|462:244|489:249,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
322,343,22096,37,0,3:0:0:0:
|
||||
259,270,22207,2,0,P|223:276|182:263,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
86,360,22540,5,8,3:0:0:0:
|
||||
15,295,22651,2,0,L|0:201,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
94,384,22985,38,0,P|118:328|112:277,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
0,211,23429,22,0,L|76:196,1,74.999998807907,12|0,3:0|0:0,0:0:0:0:
|
||||
215,134,23651,2,0,L|114:110,1,74.999998807907,12|0,3:0|0:0,0:0:0:0:
|
||||
33,124,23874,22,0,L|43:2,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
150,269,24318,2,0,L|162:194,1,74.999998807907,0|4,3:0|0:0,0:0:0:0:
|
||||
229,134,24651,6,0,L|386:164,1,112.500002503395,12|0,3:0|0:0,0:0:0:0:
|
||||
486,268,24874,37,0,0:0:0:0:
|
||||
410,119,24985,1,4,0:0:0:0:
|
||||
381,213,25096,1,0,0:0:0:0:
|
||||
512,120,25207,1,0,3:0:0:0:
|
||||
247,36,25429,6,0,L|191:25,3,37.4999994039535,4|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
185,24,25651,2,0,B|145:72|145:72|174:164,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
253,219,26096,2,0,B|281:311|281:311|228:382,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
100,363,26429,38,0,L|259:354,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
404,262,26762,1,4,0:0:0:0:
|
||||
390,352,26874,1,0,0:0:0:0:
|
||||
314,295,26985,1,8,3:0:0:0:
|
||||
425,256,27096,6,0,L|492:246,2,37.4999994039535,0|0|4,0:0|0:0|0:0,0:0:0:0:
|
||||
329,216,27318,1,0,3:0:0:0:
|
||||
193,177,27429,38,0,L|266:161,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
322,107,27651,1,4,0:0:0:0:
|
||||
322,107,27874,2,0,L|310:238,1,112.500002503395,8|4,3:0|0:0,0:0:0:0:
|
||||
110,299,28207,5,0,0:0:0:0:
|
||||
164,231,28318,2,0,B|168:303|168:303|121:338,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
30,284,28540,2,0,B|90:244|90:244|144:267,1,112.49999821186,0|0,0:0|0:0,0:0:0:0:
|
||||
148,371,28762,2,0,B|83:338|83:338|76:280,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
194,201,28985,38,0,B|207:210|207:210|227:210|227:210|243:217|243:217|265:218|265:218|282:227|282:227|305:225|305:225|325:238,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
492,114,29207,6,0,P|445:136|410:138,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
324,102,29429,2,0,P|291:68|280:29,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
418,17,29651,1,8,3:0:0:0:
|
||||
495,201,29874,1,4,3:2:0:0:
|
||||
221,136,30096,37,0,3:0:0:0:
|
||||
299,188,30207,2,0,B|316:251|316:251|271:352,1,149.999997615814,4|0,3:0|0:0,0:0:0:0:
|
||||
115,334,30540,6,0,P|11:252|167:266,1,382.500001215934,0|0,0:0|0:0,0:0:0:0:
|
||||
216,326,30985,38,0,L|304:331,1,63.7500002026557,4|0,3:0|0:0,0:0:0:0:
|
||||
280,330,31429,6,0,L|293:241,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
426,252,31651,2,0,L|439:163,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
253,158,31874,37,0,3:0:0:0:
|
||||
258,132,31985,1,0,0:0:0:0:
|
||||
337,111,32096,5,4,0:0:0:0:
|
||||
341,85,32207,1,0,0:0:0:0:
|
||||
271,30,32318,38,0,B|212:42|212:42|141:19,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
163,26,32540,2,0,L|144:181,1,149.999997615814,4|0,0:0|3:0,0:0:0:0:
|
||||
445,343,32985,22,0,B|439:234|439:234|384:269,1,149.999997615814,4|8,0:0|3:0,0:0:0:0:
|
||||
240,257,33429,2,0,B|263:148|263:148|291:205,1,149.999997615814,4|0,0:0|3:0,0:0:0:0:
|
||||
68,333,33874,2,0,B|83:233|83:233|41:256,1,149.999997615814,4|8,0:0|3:0,0:0:0:0:
|
||||
344,347,34318,22,0,B|368:372|368:372|455:355|455:355|472:308,1,149.999997615814,4|0,0:0|0:0,0:0:0:0:
|
||||
452,255,34540,2,0,B|389:212|389:212|332:273,1,149.999997615814,0|4,3:0|0:0,0:0:0:0:
|
||||
256,220,34874,5,0,0:0:0:0:
|
||||
256,220,34985,2,0,B|256:128,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
256,70,35207,2,0,B|256:162,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
112,312,35429,37,0,3:0:0:0:
|
||||
60,255,35540,2,0,B|123:212|123:212|180:273,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
169,350,35874,6,0,B|144:375|144:375|57:358|57:358|40:311,1,149.999997615814,8|0,3:0|3:0,0:0:0:0:
|
||||
62,169,36429,6,0,L|76:267,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
134,61,36762,1,8,3:0:0:0:
|
||||
201,113,36874,2,0,L|215:211,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
298,272,37207,6,0,L|315:184,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
330,114,37429,1,4,0:0:0:0:
|
||||
446,176,37540,2,0,B|404:214|404:214|307:197,1,149.999997615814,4|0,3:0|0:0,0:0:0:0:
|
||||
231,240,37874,2,0,P|223:199|231:162,1,74.999998807907,4|0,3:0|0:0,0:0:0:0:
|
||||
325,285,38096,6,0,L|154:300,1,149.999997615814,4|0,3:0|0:0,0:0:0:0:
|
||||
175,298,38540,6,0,L|163:396,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
75,208,38762,2,0,L|63:306,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
233,74,38985,37,0,3:0:0:0:
|
||||
231,98,39096,1,0,0:0:0:0:
|
||||
156,139,39207,5,4,0:0:0:0:
|
||||
155,165,39318,1,0,0:0:0:0:
|
||||
227,215,39429,38,0,P|282:209|352:230,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
336,222,39651,2,0,L|366:67,1,149.999997615814,4|0,0:0|3:0,0:0:0:0:
|
||||
81,35,40096,22,0,B|82:105|82:105|118:136|118:136|132:89,1,149.999997615814,4|8,0:0|3:0,0:0:0:0:
|
||||
272,158,40540,2,0,B|270:228|270:228|234:259|234:259|220:212,1,149.999997615814,4|0,0:0|3:0,0:0:0:0:
|
||||
423,36,40985,2,0,B|400:102|400:102|423:143|423:143|453:104,1,149.999997615814,4|8,0:0|3:0,0:0:0:0:
|
||||
512,278,41429,6,0,P|415:258|361:293,1,149.999997615814,4|0,0:0|0:0,0:0:0:0:
|
||||
359,302,41651,6,0,B|320:264|320:264|310:187,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
322,190,41874,2,0,L|449:171,1,112.49999821186,4|0,0:0|0:0,0:0:0:0:
|
||||
443,159,42096,1,8,3:0:0:0:
|
||||
240,52,42318,6,0,B|255:79|255:79|241:135,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
177,166,42540,1,0,3:0:0:0:
|
||||
163,151,42651,2,0,B|161:207|161:207|192:240|192:240|189:299,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
131,365,42985,2,0,P|198:322|280:345,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
335,377,43318,1,0,0:0:0:0:
|
||||
442,239,43429,38,0,B|456:178|456:178|422:136|422:136|427:68|427:68|449:112,1,224.999996423721,0|0,3:0|0:0,0:0:0:0:
|
||||
444,103,43874,2,0,P|402:118|356:120,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
249,28,44096,2,0,P|295:35|324:48,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
364,201,44318,5,0,0:0:0:0:
|
||||
332,195,44429,1,0,0:0:0:0:
|
||||
251,135,44540,37,0,0:0:0:0:
|
||||
281,123,44651,1,0,0:0:0:0:
|
||||
332,195,44762,6,0,B|356:269|324:333|324:333|303:293,1,179.999991645813,4|0,0:3|0:0,0:0:0:0:
|
||||
61,25,45207,38,0,L|88:158,1,112.500002503395,0|0,3:0|0:0,0:0:0:0:
|
||||
84,136,45651,1,8,3:0:0:0:
|
||||
84,136,46096,1,0,3:0:0:0:
|
||||
176,33,46207,2,0,L|164:103,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
219,207,46429,2,0,L|232:152,1,56.2500012516975,0|8,0:0|3:0,0:0:0:0:
|
||||
312,65,46651,1,0,0:0:0:0:
|
||||
312,65,46762,2,0,L|398:94,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
512,176,46985,5,0,3:0:0:0:
|
||||
421,192,47096,1,0,0:0:0:0:
|
||||
421,192,47429,1,8,3:0:0:0:
|
||||
402,357,47651,37,0,0:0:0:0:
|
||||
394,277,47762,1,0,0:0:0:0:
|
||||
328,324,47874,1,0,3:0:0:0:
|
||||
328,324,48318,1,8,3:0:0:0:
|
||||
110,357,48540,5,0,0:0:0:0:
|
||||
118,277,48651,1,0,0:0:0:0:
|
||||
184,324,48763,1,0,3:0:0:0:
|
||||
110,357,48874,1,0,0:0:0:0:
|
||||
110,357,49207,1,8,3:0:0:0:
|
||||
110,357,49651,1,0,3:0:0:0:
|
||||
0,283,49762,38,0,P|41:301|97:295,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
188,219,49985,2,0,P|168:236|137:246,1,56.2500012516975,0|8,0:0|3:0,0:0:0:0:
|
||||
49,137,50207,1,0,0:0:0:0:
|
||||
49,137,50318,2,0,P|65:184|93:205,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
107,67,50540,5,0,3:0:0:0:
|
||||
32,15,50651,1,0,0:0:0:0:
|
||||
32,15,50985,1,8,3:0:0:0:
|
||||
265,114,51207,37,0,0:0:0:0:
|
||||
254,196,51318,1,0,0:0:0:0:
|
||||
241,279,51429,1,0,3:0:0:0:
|
||||
241,279,51651,1,0,0:0:0:0:
|
||||
336,207,51762,6,0,P|397:191|371:274,1,168.750003755093,0|0,0:0|0:0,0:0:0:0:
|
||||
83,206,52318,5,0,3:0:0:0:
|
||||
83,206,52429,2,0,L|101:260,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
40,383,52651,2,0,P|70:355|90:324,1,56.2500012516975,0|8,0:0|3:0,0:0:0:0:
|
||||
214,334,52874,1,0,0:0:0:0:
|
||||
214,334,52985,2,0,P|171:322|140:304,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
151,160,53207,5,0,3:0:0:0:
|
||||
188,135,53318,1,0,0:0:0:0:
|
||||
232,129,53429,1,0,0:0:0:0:
|
||||
273,146,53540,1,0,0:0:0:0:
|
||||
339,198,53651,37,8,3:0:0:0:
|
||||
383,199,53762,1,0,0:0:0:0:
|
||||
426,185,53874,1,0,0:0:0:0:
|
||||
450,147,53985,1,0,0:0:0:0:
|
||||
444,61,54096,6,0,P|414:28|377:15,1,56.2500012516975,0|0,3:0|0:0,0:0:0:0:
|
||||
301,28,54318,2,0,P|268:48|255:77,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
189,271,54540,38,0,P|209:222|204:198,1,56.2500012516975,8|0,3:0|0:0,0:0:0:0:
|
||||
186,114,54762,2,0,P|152:74|124:68,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
27,137,54985,5,0,3:0:0:0:
|
||||
34,167,55096,1,0,0:0:0:0:
|
||||
122,204,55207,37,0,0:0:0:0:
|
||||
116,178,55318,1,0,0:0:0:0:
|
||||
48,249,55429,5,8,3:0:0:0:
|
||||
54,274,55540,1,0,0:0:0:0:
|
||||
124,329,55651,38,0,P|157:326|200:310,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
320,185,55874,5,0,3:0:0:0:
|
||||
287,175,55985,1,0,0:0:0:0:
|
||||
254,181,56096,2,0,P|258:221|264:241,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
337,347,56318,2,0,P|348:321|350:293,1,56.2500012516975,8|0,3:0|0:0,0:0:0:0:
|
||||
418,197,56540,37,0,0:0:0:0:
|
||||
418,197,56651,2,0,L|492:180,1,56.2500012516975,0|0,0:0|3:0,0:0:0:0:
|
||||
329,114,56874,2,0,L|262:94,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
436,59,57096,6,0,L|413:126,1,56.2500012516975,0|8,0:0|3:0,0:0:0:0:
|
||||
332,194,57318,2,0,L|353:259,2,56.2500012516975,0|0|0,0:0|0:0|0:0,0:0:0:0:
|
||||
202,194,57651,37,0,3:0:0:0:
|
||||
224,233,57762,1,0,0:0:0:0:
|
||||
222,279,57874,1,0,0:0:0:0:
|
||||
193,314,57985,1,0,0:0:0:0:
|
||||
144,244,58096,5,0,0:0:0:0:
|
||||
127,214,58207,1,0,0:0:0:0:
|
||||
126,180,58318,1,0,0:0:0:0:
|
||||
139,149,58429,1,0,0:0:0:0:
|
||||
224,113,58540,38,0,B|262:88|235:70|189:83|189:83|224:138|194:193,1,194.999987974167
|
||||
299,319,58874,1,0,0:0:0:0:
|
||||
299,319,58985,2,0,B|316:283|314:237|314:237|278:226|278:226|320:243|359:227,1,202.49999060154,4|0,0:0|0:0,0:0:0:0:
|
||||
428,181,59429,22,0,P|454:129|399:4,1,179.999991645813,0|0,3:0|0:0,0:0:0:0:
|
||||
418,18,59874,2,0,L|373:15,6,24.9999996026357,8|0|0|0|0|0|4,3:0|0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0:
|
||||
428,181,60207,5,0,0:0:0:0:
|
||||
352,209,60318,1,0,3:0:0:0:
|
||||
278,177,60429,1,0,0:0:0:0:
|
||||
208,225,60540,2,0,L|222:267,2,37.4999994039535,4|0|0,0:0|0:0|0:0,0:0:0:0:
|
||||
71,144,60762,38,0,L|65:109,3,24.9999996026357,8|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
145,86,60985,1,4,0:0:0:0:
|
||||
163,127,61096,1,0,0:0:0:0:
|
||||
161,171,61207,1,0,3:0:0:0:
|
||||
136,208,61318,1,0,0:0:0:0:
|
||||
99,231,61429,2,0,B|91:279|91:279|117:314,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
177,378,61651,5,8,3:0:0:0:
|
||||
177,378,61762,2,0,B|231:371|231:371|272:326|272:326|345:319,1,179.999991645813
|
||||
417,293,62096,1,0,3:0:0:0:
|
||||
438,263,62207,1,0,0:0:0:0:
|
||||
436,225,62318,1,4,0:0:0:0:
|
||||
412,196,62429,1,0,0:0:0:0:
|
||||
320,172,62540,6,0,P|307:192|291:204,1,37.4999994039535,8|0,3:0|0:0,0:0:0:0:
|
||||
291,147,62651,2,0,P|274:156|245:153,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
276,114,62762,2,0,P|250:107|234:94,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
283,81,62874,2,0,P|265:61|260:45,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
365,31,62985,38,0,P|398:44|442:49,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
512,169,63207,2,0,P|466:163|421:176,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
350,107,63429,1,8,3:0:0:0:
|
||||
293,237,63540,38,0,L|276:158,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
428,269,63762,2,0,B|373:275|373:275|338:249|338:249|267:255,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
191,318,64096,2,0,B|182:355|182:355|212:395,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
192,186,64318,5,8,3:0:0:0:
|
||||
135,253,64429,2,0,L|56:270,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
24,136,64762,38,0,P|69:76|158:75,1,157.499992690086,0|0,3:0|0:0,0:0:0:0:
|
||||
160,80,64985,6,0,P|193:102|255:102,1,84.3750018775463,4|0,0:0|0:0,0:0:0:0:
|
||||
276,34,65207,38,0,L|290:212,1,157.499992690086,8|0,3:0|0:0,0:0:0:0:
|
||||
291,219,65429,6,0,L|311:132,1,84.3750018775463,4|0,0:0|0:0,0:0:0:0:
|
||||
381,111,65651,38,0,B|418:126|418:126|460:126,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
221,163,65874,2,0,B|186:143|186:143|139:153,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
41,231,66096,1,8,3:0:0:0:
|
||||
49,267,66207,1,0,0:0:0:0:
|
||||
56,303,66318,1,4,0:0:0:0:
|
||||
67,288,66429,1,4,0:0:0:0:
|
||||
77,270,66540,6,0,P|171:255|72:350,1,337.500007510185,0|0,0:0|0:0,0:0:0:0:
|
||||
95,356,66985,38,0,L|185:343,1,74.999998807907,8|4,3:0|0:0,0:0:0:0:
|
||||
274,286,67318,6,0,B|289:324|289:324|268:378,1,74.999998807907,0|0,0:0|3:0,0:0:0:0:
|
||||
191,227,67540,1,0,0:0:0:0:
|
||||
255,168,67651,2,0,L|264:116,2,37.4999994039535,4|0|0,0:0|0:0|0:0,0:0:0:0:
|
||||
147,83,67874,2,0,L|154:108,3,24.9999996026357,8|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
80,148,68096,38,0,L|98:224,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
125,356,68318,1,0,3:0:0:0:
|
||||
0,319,68429,1,0,0:0:0:0:
|
||||
0,319,68540,2,0,L|76:294,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
277,219,68762,5,8,3:0:0:0:
|
||||
277,219,68874,2,0,B|327:199|327:199|293:138|197:173,1,179.999991645813,0|0,0:0|0:0,0:0:0:0:
|
||||
157,273,69207,37,0,3:0:0:0:
|
||||
175,316,69318,1,0,0:0:0:0:
|
||||
212,334,69429,1,4,0:0:0:0:
|
||||
254,333,69540,1,0,0:0:0:0:
|
||||
332,268,69651,38,0,P|333:237|343:213,1,37.4999994039535,8|0,3:0|0:0,0:0:0:0:
|
||||
373,265,69762,2,0,P|386:239|404:232,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
413,284,69874,2,0,P|430:269|454:269,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
433,318,69985,2,0,P|452:320|474:337,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
401,384,70096,6,0,P|353:378|319:346,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
251,251,70318,2,0,P|240:196|260:154,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
401,18,70540,1,8,3:0:0:0:
|
||||
401,18,70651,2,0,P|409:54|398:90,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
327,193,70874,2,0,L|304:45,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
290,26,71207,6,0,L|308:144,1,104.999995126724,0|0,0:0|0:0,0:0:0:0:
|
||||
272,302,71429,2,0,L|187:288,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
33,217,71651,37,4,0:0:0:0:
|
||||
27,187,71762,1,4,0:0:0:0:
|
||||
20,157,71874,2,0,B|103:140|103:140|162:58,1,157.499992690086,0|0,3:0|0:0,0:0:0:0:
|
||||
145,82,72096,6,0,L|218:75,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
336,136,72318,38,0,P|331:213|231:208,1,157.499992690086
|
||||
263,232,72540,6,0,L|278:300,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
183,384,72762,2,0,L|172:307,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
37,140,72985,38,0,B|10:168|10:168|17:204|17:204|54:220|54:220|89:196|89:196|87:157|87:157|57:138,1,225.00000500679
|
||||
275,372,73651,6,0,P|320:352|387:369,1,112.500002503395
|
||||
380,364,74096,2,0,L|436:358,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
495,271,74318,2,0,L|424:282,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
339,270,74540,1,0,0:0:0:0:
|
||||
339,270,74651,1,0,0:0:0:0:
|
||||
339,270,74762,2,0,L|329:196,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
408,46,74985,38,0,L|392:120,1,56.2500012516975
|
||||
220,230,75207,2,0,L|209:156,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
282,7,75429,37,0,0:0:0:0:
|
||||
300,98,75540,1,0,0:0:0:0:
|
||||
197,25,75651,5,0,0:0:0:0:
|
||||
222,103,75762,1,0,0:0:0:0:
|
||||
126,69,75874,5,0,0:0:0:0:
|
||||
153,134,75985,1,0,0:0:0:0:
|
||||
76,145,76096,5,0,0:0:0:0:
|
||||
116,179,76207,1,0,0:0:0:0:
|
||||
70,222,76318,5,0,0:0:0:0:
|
||||
111,222,76429,1,0,0:0:0:0:
|
||||
134,253,76540,6,0,P|135:298|126:314,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
21,384,76762,2,0,P|124:354|260:391,1,224.999996423721,0|0,0:0|0:0,0:0:0:0:
|
||||
384,366,77207,22,0,L|394:268,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
499,62,77429,2,0,L|486:135,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
507,237,77651,2,0,P|450:231|388:184,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
404,203,77874,2,0,L|313:217,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
113,212,78096,6,0,P|128:267|111:328,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
115,319,78318,2,0,L|213:340,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
274,371,78540,38,0,L|257:186,1,179.999991645813,8|0,3:0|0:0,0:0:0:0:
|
||||
128,139,78874,1,0,0:0:0:0:
|
||||
128,139,78985,6,0,L|230:128,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
365,34,79207,37,4,0:0:0:0:
|
||||
430,114,79318,1,0,0:0:0:0:
|
||||
361,184,79429,2,0,P|304:170|277:110,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
278,126,79651,2,0,L|189:133,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
64,263,79874,6,0,B|37:230|37:230|50:143,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
66,119,80096,2,0,L|80:210,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
71,361,80318,38,0,B|135:350|135:350|182:305|182:305|243:297,1,179.999991645813,8|0,3:0|0:0,0:0:0:0:
|
||||
302,247,80651,1,0,0:0:0:0:
|
||||
222,211,80762,1,0,3:0:0:0:
|
||||
478,344,80985,5,4,0:0:0:0:
|
||||
491,309,81096,5,0,0:0:0:0:
|
||||
498,265,81207,5,8,3:0:0:0:
|
||||
485,223,81318,5,0,0:0:0:0:
|
||||
458,179,81429,5,4,0:0:0:0:
|
||||
418,147,81540,5,0,0:0:0:0:
|
||||
352,126,81651,5,0,3:0:0:0:
|
||||
281,149,81762,5,0,0:0:0:0:
|
||||
239,221,81874,5,4,0:0:0:0:
|
||||
159,262,81985,5,0,0:0:0:0:
|
||||
66,234,82096,5,8,3:0:0:0:
|
||||
11,145,82207,5,0,0:0:0:0:
|
||||
55,33,82318,5,4,0:0:0:0:
|
||||
273,44,82540,37,0,3:0:0:0:
|
||||
320,103,82651,1,0,0:0:0:0:
|
||||
394,118,82762,1,4,0:0:0:0:
|
||||
468,100,82874,1,0,0:0:0:0:
|
||||
507,36,82985,1,8,3:0:0:0:
|
||||
495,19,83207,5,4,0:0:0:0:
|
||||
335,83,83318,1,0,0:0:0:0:
|
||||
453,81,83429,1,0,3:0:0:0:
|
||||
283,24,83540,2,0,P|196:37|141:120,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
60,238,83874,1,8,3:0:0:0:
|
||||
21,164,83985,2,0,P|59:149|175:193,1,149.999997615814,0|8,0:0|3:0,0:0:0:0:
|
||||
252,206,84318,38,0,P|271:160|264:125,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
139,257,84540,2,0,P|131:302|149:340,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
240,379,84762,2,0,B|330:360|330:360|298:344,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
312,351,84985,2,0,P|279:321|270:287,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
359,165,85207,6,0,B|389:202|389:202|368:282,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
373,265,85429,2,0,L|454:282,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
498,139,85651,38,0,P|446:120|396:0,1,179.999991645813,8|0,3:0|0:0,0:0:0:0:
|
||||
394,13,85985,1,0,0:0:0:0:
|
||||
301,92,86096,6,0,L|214:83,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
66,66,86318,1,4,0:0:0:0:
|
||||
13,136,86429,1,0,0:0:0:0:
|
||||
72,193,86540,2,0,P|120:210|190:178,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
176,192,86762,2,0,P|154:237|160:288,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
309,370,86985,37,0,3:0:0:0:
|
||||
359,310,87096,1,0,0:0:0:0:
|
||||
283,297,87207,2,0,L|203:318,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
4,203,87429,2,0,B|55:211|55:211|82:255|82:255|134:266,1,149.999997615814,8|0,3:0|0:0,0:0:0:0:
|
||||
238,217,87762,1,0,0:0:0:0:
|
||||
183,120,87874,6,0,L|89:111,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
98,33,88096,2,0,L|23:26,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
306,182,88318,38,0,L|400:173,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
391,95,88540,2,0,L|465:88,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
232,28,88762,2,0,L|220:92,1,37.4999994039535,0|0,3:0|0:0,0:0:0:0:
|
||||
243,39,88874,2,0,L|231:103,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
256,50,88985,2,0,L|251:87,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
485,87,89207,6,0,L|493:51,3,37.4999994039535,8|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
396,120,89429,2,0,L|411:197,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
471,317,89651,38,0,P|411:299|320:336,1,149.999997615814,0|4,3:0|0:0,0:0:0:0:
|
||||
61,239,90096,2,0,P|121:221|212:258,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
367,21,90540,6,0,P|336:57|328:104,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
163,96,90762,2,0,P|194:132|202:179,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
190,346,90985,37,8,3:0:0:0:
|
||||
328,272,91096,1,0,0:0:0:0:
|
||||
154,272,91207,5,8,3:0:0:0:
|
||||
365,338,91318,1,0,0:0:0:0:
|
||||
257,382,91429,38,0,B|290:333|224:286|269:219,1,149.999997615814,4|4,3:0|0:0,0:0:0:0:
|
||||
325,196,91762,1,0,0:0:0:0:
|
||||
325,196,91874,2,0,P|365:210|436:184,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
430,190,92096,2,0,B|418:110,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
313,19,92318,2,0,L|190:36,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
201,34,92540,2,0,B|214:117,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
209,252,92762,5,8,3:0:0:0:
|
||||
156,261,92874,1,0,0:0:0:0:
|
||||
112,231,92985,1,4,0:0:0:0:
|
||||
60,222,93096,1,0,0:0:0:0:
|
||||
13,247,93207,38,0,P|4:288|19:328,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
173,186,93429,1,4,0:0:0:0:
|
||||
215,120,93540,1,0,0:0:0:0:
|
||||
162,49,93651,2,0,P|125:39|76:61,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
234,138,93874,2,0,P|273:157|313:148,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
385,39,94096,5,0,3:0:0:0:
|
||||
337,286,94318,2,0,L|322:373,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
409,327,94540,2,0,P|418:277|280:230,1,224.999996423721,8|0,3:0|0:0,0:0:0:0:
|
||||
239,319,94985,2,0,P|218:357|173:373,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
34,344,95207,37,4,0:0:0:0:
|
||||
21,309,95318,5,0,0:0:0:0:
|
||||
14,265,95429,5,8,3:0:0:0:
|
||||
27,223,95540,5,0,0:0:0:0:
|
||||
54,179,95651,5,4,0:0:0:0:
|
||||
94,147,95762,5,0,0:0:0:0:
|
||||
160,126,95873,5,0,3:0:0:0:
|
||||
231,149,95984,5,0,0:0:0:0:
|
||||
273,221,96096,5,4,0:0:0:0:
|
||||
353,262,96207,5,0,0:0:0:0:
|
||||
446,234,96318,5,8,3:0:0:0:
|
||||
501,145,96429,5,0,0:0:0:0:
|
||||
450,36,96540,5,4,0:0:0:0:
|
||||
239,44,96762,5,0,3:0:0:0:
|
||||
192,103,96873,1,0,0:0:0:0:
|
||||
118,118,96984,1,4,0:0:0:0:
|
||||
44,100,97096,1,0,0:0:0:0:
|
||||
5,36,97207,1,8,3:0:0:0:
|
||||
17,19,97429,37,4,0:0:0:0:
|
||||
146,51,97540,1,0,0:0:0:0:
|
||||
29,122,97651,2,0,L|39:193,1,56.2499991059302,0|0,3:0|0:0,0:0:0:0:
|
||||
44,197,97874,6,0,P|100:231|176:201,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
301,160,98096,38,0,P|329:140|382:137,1,84.3750018775463,8|0,3:0|0:0,0:0:0:0:
|
||||
398,147,98318,6,0,B|431:187|431:187|415:279,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
265,371,98540,38,0,L|180:361,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
127,202,98762,2,0,L|141:113,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
193,260,98985,2,0,P|144:291|68:278,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
91,290,99207,2,0,L|79:373,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
20,184,99429,6,0,B|4:141|4:141|27:66,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
23,78,99651,2,0,L|109:91,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
271,74,99874,2,0,P|254:31|222:12,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
186,180,100096,2,0,P|232:175|260:147,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
132,63,100318,37,0,3:0:0:0:
|
||||
253,157,100540,1,4,0:0:0:0:
|
||||
285,167,100651,1,0,0:0:0:0:
|
||||
357,129,100762,5,8,3:0:0:0:
|
||||
389,139,100873,1,0,0:0:0:0:
|
||||
422,148,100985,2,0,P|407:200|416:233,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
459,377,101207,38,0,P|472:333|459:295,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
398,242,101429,2,0,L|314:257,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
165,354,101651,2,0,P|116:332|211:264,1,224.999996423721,8|0,3:0|0:0,0:0:0:0:
|
||||
302,165,102096,6,0,L|292:89,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
392,91,102318,2,0,L|382:14,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
192,229,102540,38,0,L|212:136,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
107,172,102762,2,0,L|127:79,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
314,332,102985,6,0,L|305:278,1,37.4999994039535,0|0,3:0|0:0,0:0:0:0:
|
||||
343,345,103096,2,0,L|334:291,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
370,358,103207,2,0,L|361:304,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
380,117,103429,38,0,L|374:75,3,37.4999994039535,8|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
444,166,103651,2,0,P|417:188|346:191,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
392,2,103874,2,0,P|424:14|462:74,1,74.999998807907,4|0,3:0|0:0,0:0:0:0:
|
||||
271,129,104096,2,0,P|265:94|298:31,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
505,113,104318,5,8,3:0:0:0:
|
||||
269,217,104540,38,0,L|216:216,3,37.4999994039535,0|0|0|0,3:0|3:0|3:0|3:0,0:0:0:0:
|
||||
360,220,104762,1,0,3:0:0:0:
|
||||
296,384,104874,1,4,3:0:0:0:
|
||||
102,307,105096,5,0,0:0:0:0:
|
||||
102,307,105207,2,0,B|206:381|258:244|374:330,1,269.999987468719,12|0,3:0|0:0,0:0:0:0:
|
||||
439,319,105651,6,0,P|379:336|396:236,1,168.750003755093,0|0,3:0|0:0,0:0:0:0:
|
||||
373,258,106096,6,0,P|374:315|443:283,1,112.500002503395,8|0,3:0|0:0,0:0:0:0:
|
||||
420,323,106651,37,0,0:0:0:0:
|
||||
469,245,106763,1,4,0:0:0:0:
|
||||
508,322,106874,1,0,0:0:0:0:
|
||||
379,245,106985,1,8,3:0:0:0:
|
||||
483,105,107207,6,0,L|474:40,3,56.2500012516975,4|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
462,30,107429,38,0,P|401:56|319:25,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
272,120,107874,2,0,P|184:91|118:125,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
103,213,108207,2,0,B|128:232|128:232|269:200,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
393,187,108540,2,0,L|385:286,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
333,338,108763,1,8,3:0:0:0:
|
||||
467,307,108874,6,0,L|509:297,2,37.4999994039535,0|0|4,0:0|0:0|0:0,0:0:0:0:
|
||||
409,380,109096,1,0,0:0:0:0:
|
||||
300,257,109207,38,0,P|279:218|281:171,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
401,118,109429,1,4,0:0:0:0:
|
||||
401,118,109651,6,0,L|315:109,1,74.999998807907,8|4,3:0|0:0,0:0:0:0:
|
||||
256,15,109985,37,0,0:0:0:0:
|
||||
175,121,110096,2,0,P|162:60|109:16,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
128,26,110318,2,0,P|106:86|47:122,1,112.49999821186,0|0,0:0|0:0,0:0:0:0:
|
||||
69,114,110540,2,0,P|135:105|185:131,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
160,223,110762,6,0,B|142:230|142:230|120:228|120:228|95:239|95:239|71:235|71:235|49:244|49:244|22:249,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
193,334,110985,38,0,P|216:310|242:301,1,56.2500012516975,0|0,3:0|0:0,0:0:0:0:
|
||||
335,325,111207,2,0,P|366:353|378:379,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
273,383,111429,2,0,L|304:213,1,168.750003755093,0|0,0:0|0:0,0:0:0:0:
|
||||
383,255,111874,22,0,B|422:273|422:273|476:273,1,74.999998807907,8|0,3:0|3:0,0:0:0:0:
|
||||
209,219,112096,2,0,B|169:221|169:221|131:206,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
403,147,112318,2,0,B|352:114|352:114|337:43|337:43|295:109|295:109|234:115,1,269.999987468719,8|0,3:0|0:0,0:0:0:0:
|
||||
-1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 9.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 644 KiB |
@@ -4,8 +4,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -19,9 +19,23 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
public class StackingTest
|
||||
{
|
||||
[Test]
|
||||
public void TestStackingEdgeCaseOne()
|
||||
public void TestStacking()
|
||||
{
|
||||
using (var stream = new MemoryStream(@"
|
||||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(beatmap_data)))
|
||||
using (var reader = new LineBufferedReader(stream))
|
||||
{
|
||||
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>());
|
||||
|
||||
var objects = converted.HitObjects.ToList();
|
||||
|
||||
// The last hitobject triggers the stacking
|
||||
for (int i = 0; i < objects.Count - 1; i++)
|
||||
Assert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private const string beatmap_data = @"
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
@@ -48,65 +62,6 @@ SliderTickRate:0.5
|
||||
311,185,218471,2,0,L|325:209,1,25
|
||||
311,185,218671,2,0,L|304:212,1,25
|
||||
311,185,240271,5,0,0:0:0:0:
|
||||
"u8.ToArray()))
|
||||
using (var reader = new LineBufferedReader(stream))
|
||||
{
|
||||
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>());
|
||||
|
||||
var objects = converted.HitObjects.ToList();
|
||||
|
||||
// The last hitobject triggers the stacking
|
||||
for (int i = 0; i < objects.Count - 1; i++)
|
||||
ClassicAssert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStackingEdgeCaseTwo()
|
||||
{
|
||||
using (var stream = new MemoryStream(@"
|
||||
osu file format v14
|
||||
// extracted from https://osu.ppy.sh/beatmapsets/365006#osu/801165
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.2
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:6
|
||||
CircleSize:4
|
||||
OverallDifficulty:8
|
||||
ApproachRate:9.3
|
||||
SliderMultiplier:2
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
5338,444.444444444444,4,2,0,50,1,0
|
||||
82893,-76.9230769230769,4,2,8,50,0,0
|
||||
85115,-76.9230769230769,4,2,0,50,0,0
|
||||
85337,-100,4,2,8,60,0,0
|
||||
85893,-100,4,2,7,60,0,0
|
||||
86226,-100,4,2,8,60,0,0
|
||||
88893,-58.8235294117647,4,1,8,70,0,1
|
||||
|
||||
[HitObjects]
|
||||
427,124,84226,1,0,3:0:0:0:
|
||||
427,124,84337,1,0,3:0:0:0:
|
||||
427,124,84449,1,8,0:0:0:0:
|
||||
"u8.ToArray()))
|
||||
using (var reader = new LineBufferedReader(stream))
|
||||
{
|
||||
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>());
|
||||
|
||||
var objects = converted.HitObjects.ToList();
|
||||
|
||||
Assert.That(objects, Has.Count.EqualTo(3));
|
||||
|
||||
// The last hitobject triggers the stacking
|
||||
for (int i = 0; i < objects.Count - 1; i++)
|
||||
ClassicAssert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
|
||||
}
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[HeadlessTest]
|
||||
public partial class TestSceneAutoGeneration : OsuTestScene
|
||||
{
|
||||
[TestCase(-1, true)]
|
||||
[TestCase(0, false)]
|
||||
[TestCase(1, false)]
|
||||
public void TestAlternating(double offset, bool shouldAlternate)
|
||||
{
|
||||
const double first_object_time = 1000;
|
||||
double secondObjectTime = first_object_time + AutoGenerator.KEY_UP_DELAY + OsuAutoGenerator.MIN_FRAME_SEPARATION_FOR_ALTERNATING + offset;
|
||||
|
||||
var beatmap = new OsuBeatmap();
|
||||
beatmap.HitObjects.Add(new HitCircle { StartTime = first_object_time });
|
||||
beatmap.HitObjects.Add(new HitCircle { StartTime = secondObjectTime });
|
||||
|
||||
var generated = new OsuAutoGenerator(beatmap, []).Generate();
|
||||
var frames = generated.Frames.OfType<OsuReplayFrame>().ToList();
|
||||
|
||||
Assert.That(frames.Exists(f => f.Time == first_object_time && f.Actions.SingleOrDefault() == OsuAction.LeftButton));
|
||||
Assert.That(frames.Exists(f => f.Time == first_object_time + AutoGenerator.KEY_UP_DELAY && !f.Actions.Any()));
|
||||
|
||||
Assert.That(frames.Exists(f => f.Time == secondObjectTime && f.Actions.SingleOrDefault() == (shouldAlternate ? OsuAction.RightButton : OsuAction.LeftButton)));
|
||||
Assert.That(frames.Exists(f => f.Time == secondObjectTime + AutoGenerator.KEY_UP_DELAY && !f.Actions.Any()));
|
||||
}
|
||||
|
||||
[TestCase(300)]
|
||||
[TestCase(600)]
|
||||
[TestCase(1200)]
|
||||
public void TestAlternatingSpecificBPM(double bpm)
|
||||
{
|
||||
const double first_object_time = 1000;
|
||||
double secondObjectTime = first_object_time + 60000 / bpm;
|
||||
|
||||
var beatmap = new OsuBeatmap();
|
||||
beatmap.HitObjects.Add(new HitCircle { StartTime = first_object_time });
|
||||
beatmap.HitObjects.Add(new HitCircle { StartTime = secondObjectTime });
|
||||
|
||||
var generated = new OsuAutoGenerator(beatmap, []).Generate();
|
||||
var frames = generated.Frames.OfType<OsuReplayFrame>().ToList();
|
||||
|
||||
Assert.That(frames.Exists(f => f.Time == first_object_time && f.Actions.SingleOrDefault() == OsuAction.LeftButton));
|
||||
Assert.That(frames.Exists(f => f.Time == first_object_time + AutoGenerator.KEY_UP_DELAY && !f.Actions.Any()));
|
||||
|
||||
Assert.That(frames.Exists(f => f.Time == secondObjectTime && f.Actions.SingleOrDefault() == OsuAction.RightButton));
|
||||
Assert.That(frames.Exists(f => f.Time == secondObjectTime + AutoGenerator.KEY_UP_DELAY && !f.Actions.Any()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public partial class TestSceneGameplayCursorSizeChange : PlayerTestScene
|
||||
{
|
||||
private const float initial_cursor_size = 1f;
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
|
||||
[Resolved]
|
||||
private SkinManager? skins { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
if (skins != null) skins.CurrentSkinInfo.Value = skins.DefaultClassicSkin.SkinInfo;
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("Set gameplay cursor size: 1", () => LocalConfig.SetValue(OsuSetting.GameplayCursorSize, initial_cursor_size));
|
||||
AddStep("resume player", () => Player.GameplayClockContainer.Start());
|
||||
AddUntilStep("clock running", () => Player.GameplayClockContainer.IsRunning);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPausedChangeCursorSize()
|
||||
{
|
||||
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
|
||||
AddStep("move cursor to top left", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft));
|
||||
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
|
||||
AddStep("move cursor to top right", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopRight));
|
||||
AddStep("press escape", () => InputManager.Key(Key.Escape));
|
||||
|
||||
AddSliderStep("cursor size", 0.1f, 2f, 1f, v => LocalConfig.SetValue(OsuSetting.GameplayCursorSize, v));
|
||||
}
|
||||
|
||||
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, false);
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
Debug.Assert(drawableHitObject.HitObject.HitWindows != null);
|
||||
|
||||
double delay = drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.WindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current;
|
||||
scheduledTasks.Add(Scheduler.AddDelayed(drawableHitObject.TriggerJudgement, delay));
|
||||
scheduledTasks.Add(Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(), delay));
|
||||
|
||||
return drawableHitObject;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
@@ -11,7 +10,6 @@ using osu.Framework.Input.States;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Testing.Input;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
@@ -60,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
foreach (var smokeContainer in smokeContainers)
|
||||
{
|
||||
if (smokeContainer.Children.OfType<SkinnableDrawable>().Any())
|
||||
if (smokeContainer.Children.Count != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +191,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
public void TestRewind()
|
||||
{
|
||||
AddStep("set manual clock", () => manualClock = new ManualClock
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="NUnit" Version="4.5.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageReference Include="Moq" Version="4.18.4" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
continue;
|
||||
|
||||
double endTime = stackBaseObject.GetEndTime();
|
||||
float stackThreshold = calculateStackThreshold(beatmap, objectN);
|
||||
double stackThreshold = objectN.TimePreempt * beatmap.StackLeniency;
|
||||
|
||||
if (objectN.StartTime - endTime > stackThreshold)
|
||||
// We are no longer within stacking range of the next object.
|
||||
@@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
OsuHitObject objectI = hitObjects[i];
|
||||
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
||||
|
||||
float stackThreshold = calculateStackThreshold(beatmap, objectI);
|
||||
double stackThreshold = objectI.TimePreempt * beatmap.StackLeniency;
|
||||
|
||||
/* If this object is a hitcircle, then we enter this "special" case.
|
||||
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
||||
@@ -151,10 +151,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
|
||||
double endTime = objectN.GetEndTime();
|
||||
|
||||
// truncation to integer is required to match stable
|
||||
// compare https://github.com/peppy/osu-stable-reference/blob/08e3dafd525934cf48880b08e91c24ce4ad8b761/osu!/GameplayElements/HitObjectManager.cs#L1725
|
||||
// - both quantities being subtracted there are integers
|
||||
if ((int)objectI.StartTime - (int)endTime > stackThreshold)
|
||||
if (objectI.StartTime - endTime > stackThreshold)
|
||||
// We are no longer within stacking range of the previous object.
|
||||
break;
|
||||
|
||||
@@ -235,7 +232,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
|
||||
for (int j = i + 1; j < hitObjects.Count; j++)
|
||||
{
|
||||
float stackThreshold = calculateStackThreshold(beatmap, hitObjects[i]);
|
||||
double stackThreshold = hitObjects[i].TimePreempt * beatmap.StackLeniency;
|
||||
|
||||
if (hitObjects[j].StartTime - stackThreshold > startTime)
|
||||
break;
|
||||
@@ -267,17 +264,5 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Truncation of <see cref="OsuHitObject.TimePreempt"/> to <see cref="int"/>, as well as keeping the result as <see cref="float"/>, are both done
|
||||
/// <a href="https://github.com/peppy/osu-stable-reference/blob/08e3dafd525934cf48880b08e91c24ce4ad8b761/osu!/GameplayElements/HitObjectManager.cs#L1652">
|
||||
/// for the purposes of stable compatibility
|
||||
/// </a>.
|
||||
/// Note that for top-level objects <see cref="OsuHitObject.TimePreempt"/> is supposed to be integral anyway;
|
||||
/// see <see cref="OsuHitObject.ApplyDefaultsToSelf"/> using <see cref="IBeatmapDifficultyInfo.DifficultyRangeInt"/> when calculating it.
|
||||
/// Slider ticks and end circles are the exception to that, but they do not matter for stacking.
|
||||
/// </remarks>
|
||||
private static float calculateStackThreshold(IBeatmap beatmap, OsuHitObject hitObject)
|
||||
=> (int)hitObject.TimePreempt * beatmap.StackLeniency;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user