1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-14 07:24:04 +08:00

Compare commits

..

119 Commits

319 changed files with 8598 additions and 1846 deletions
+2 -2
View File
@@ -10,7 +10,7 @@
"rollForward": false
},
"codefilesanity": {
"version": "0.0.37",
"version": "0.0.41",
"commands": [
"CodeFileSanity"
],
@@ -24,4 +24,4 @@
"rollForward": false
}
}
}
}
+4
View File
@@ -2,6 +2,10 @@
Thank you for showing interest in the development of osu!. We aim to provide a good collaborating environment for everyone involved, and as such have decided to list some of the most important things to keep in mind in the process. The guidelines below have been chosen based on past experience.
## Foreword on AI usage
Our team believes in **human contributions**. Any contribution be it an issue report or a pull request which is created by, documented by, or aided by AI/LLM usage will typically be **closed and locked without further discussion**.
## Table of contents
1. [Reporting bugs](#reporting-bugs)
+2
View File
@@ -138,6 +138,8 @@ If you wish to help with localisation efforts, head over to [crowdin](https://cr
We love to reward quality contributions. If you have made a large contribution, or are a regular contributor, you are welcome to [submit an expense via opencollective](https://opencollective.com/ppy/expenses/new). If you have any questions, feel free to [reach out to peppy](mailto:pe@ppy.sh) before doing so.
Our team believes in **human contributions**. Any contribution be it an issue report or a pull request which is created by, documented by, or aided by AI/LLM usage will typically be **closed and locked without further discussion**.
## Licence
*osu!*'s code and framework are licensed under the [MIT licence](https://opensource.org/licenses/MIT). Please see [the licence file](LICENCE) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source.
+1 -1
View File
@@ -10,7 +10,7 @@
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.Android" Version="2026.318.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2026.513.0" />
</ItemGroup>
<PropertyGroup>
<!-- Fody does not handle Android build well, and warns when unchanged.
+18 -7
View File
@@ -20,13 +20,24 @@ using Uri = Android.Net.Uri;
namespace osu.Android
{
[Activity(ConfigurationChanges = DEFAULT_CONFIG_CHANGES, Exported = true, LaunchMode = DEFAULT_LAUNCH_MODE, MainLauncher = true)]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osr", DataHost = "*", DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-replay")]
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, Label = "Import beatmap", DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*",
DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, Label = "Import skin", DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*",
DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, Label = "Import replay", DataScheme = "content", DataPathPattern = ".*\\\\.osr", DataHost = "*",
DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, Label = "Import beatmap", DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, Label = "Import skin", DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, Label = "Import replay", DataScheme = "content", DataMimeType = "application/x-osu-replay")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, Label = "Import file", DataScheme = "content", DataMimeTypes = new[]
{
"application/zip",
"application/octet-stream",
"application/download",
"application/x-zip",
"application/x-zip-compressed",
})]
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, Label = "Import", DataMimeTypes = new[]
{
"application/zip",
"application/octet-stream",
+1 -1
View File
@@ -190,7 +190,7 @@ namespace osu.Desktop
}
// user party
if (!hideIdentifiableInformation && multiplayerClient.Room != null && multiplayerClient.Room.Settings.MatchType != MatchType.Matchmaking)
if (!hideIdentifiableInformation && multiplayerClient.Room != null && !multiplayerClient.Room.Settings.MatchType.IsMatchmakingType())
{
MultiplayerRoom room = multiplayerClient.Room;
@@ -0,0 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Newtonsoft.Json;
namespace osu.Desktop.IPC.Messages
{
public class HitCountMessage : OsuWebSocketMessage
{
[JsonProperty("new_hits")]
public long NewHits { get; init; }
}
}
@@ -0,0 +1,19 @@
// 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 Newtonsoft.Json;
using osu.Framework.Extensions.TypeExtensions;
namespace osu.Desktop.IPC.Messages
{
public abstract class OsuWebSocketMessage
{
[JsonProperty("type")]
public string Type { get; }
protected OsuWebSocketMessage()
{
Type = GetType().ReadableName();
}
}
}
+75
View File
@@ -0,0 +1,75 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using System.Threading;
using osu.Desktop.IPC.Messages;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Game.Configuration;
using osu.Game.IPC;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using JsonConvert = Newtonsoft.Json.JsonConvert;
namespace osu.Desktop.IPC
{
public partial class OsuWebSocketProvider : Component
{
private WebSocketServer? server;
private readonly Bindable<ScoreInfo> lastLocalScore = new Bindable<ScoreInfo>();
[BackgroundDependencyLoader]
private void load(SessionStatics sessionStatics)
{
server = new WebSocketServer(49727);
server.StartAsync().FireAndForget(onError: ex => Logger.Error(ex, "Failed to start websocket"));
sessionStatics.BindWith(Static.LastLocalUserScore, lastLocalScore);
}
protected override void LoadComplete()
{
base.LoadComplete();
lastLocalScore.BindValueChanged(val =>
{
if (val.NewValue == null)
return;
if (server?.IsRunning != true)
return;
var msg = new HitCountMessage { NewHits = val.NewValue.Statistics.Where(kv => kv.Key.IsBasic() && kv.Key.IsHit()).Sum(kv => kv.Value) };
broadcast(msg);
});
}
private void broadcast(OsuWebSocketMessage message)
{
if (server?.IsRunning != true)
return;
string messageString = JsonConvert.SerializeObject(message);
server.BroadcastAsync(messageString).FireAndForget();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (server?.IsRunning == true)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(10));
server.StopAsync(cts.Token).WaitSafely();
server = null;
}
}
}
}
+6
View File
@@ -6,6 +6,7 @@ using System.IO;
using System.Reflection;
using System.Runtime.Versioning;
using Microsoft.Win32;
using osu.Desktop.IPC;
using osu.Desktop.Performance;
using osu.Desktop.Security;
using osu.Framework.Platform;
@@ -35,6 +36,8 @@ namespace osu.Desktop
public bool IsFirstRun { get; init; }
public bool EnableWebSocketServer { get; init; }
public OsuGameDesktop(string[]? args = null)
: base(args)
{
@@ -148,6 +151,9 @@ namespace osu.Desktop
osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this);
archiveImportIPCChannel = new ArchiveImportIPCChannel(Host, this);
if (EnableWebSocketServer)
Add(new OsuWebSocketProvider());
}
public override void SetHost(GameHost host)
+2 -1
View File
@@ -140,7 +140,8 @@ namespace osu.Desktop
{
host.Run(new OsuGameDesktop(args)
{
IsFirstRun = isFirstRun
IsFirstRun = isFirstRun,
EnableWebSocketServer = Environment.GetEnvironmentVariable("OSU_WEBSOCKET_SERVER") == "1",
});
}
}
+2 -1
View File
@@ -25,7 +25,8 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="10.0.5" />
<PackageReference Include="DiscordRichPresence" Version="1.6.1.70" />
<!-- Held back due to invite bug in newer versions. See https://github.com/Lachee/discord-rpc-csharp/issues/286-->
<PackageReference Include="DiscordRichPresence" Version="1.5.0.51" />
<PackageReference Include="Velopack" Version="0.0.1298" />
</ItemGroup>
<ItemGroup Label="Resources">
@@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
private readonly Path drawablePath;
private readonly List<(double Time, float X)> vertices = new List<(double, float)>();
private readonly List<Vector2> sliderVertices = new List<Vector2>();
public ScrollingPath()
{
@@ -47,9 +48,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
private void computeTimeXs(JuiceStream hitObject)
{
vertices.Clear();
var sliderVertices = new List<Vector2>();
hitObject.Path.GetPathToProgress(sliderVertices, 0, 1);
sliderVertices.Clear();
sliderVertices.AddRange(hitObject.Path.CalculatedPath);
if (sliderVertices.Count == 0)
return;
@@ -14,6 +14,9 @@ namespace osu.Game.Rulesets.Catch.Edit
{
private readonly List<ICheck> checks = new List<ICheck>
{
// Audio
new CheckCatchFewHitsounds(),
// Compose
new CheckBananaShowerGap(),
new CheckConcurrentObjects(),
@@ -0,0 +1,14 @@
// 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.Edit.Checks;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Edit.Checks
{
public class CheckCatchFewHitsounds : CheckFewHitsounds
{
protected override bool IsExcludedFromHitsounding(HitObject hitObject) => hitObject is BananaShower;
}
}
@@ -117,6 +117,15 @@ namespace osu.Game.Rulesets.Catch.Edit.Setup
Beatmap.Difficulty.CircleSize = circleSizeSlider.Current.Value;
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
Beatmap.Difficulty.ApproachRate = approachRateSlider.Current.Value;
// in lazer catch, Overall Difficulty does *nothing* - as it should be in a sane world.
// in stable, it does *one extremely specific thing* which is influence the infamous `difficultyPeppyStars`
// which in turn affects score V1 (see `LegacyRulesetExtensions.CalculateDifficultyPeppyStars()`).
// there is a Ranking Criteria rule saying that Overall Difficulty and Approach Rate should match:
// https://osu.ppy.sh/wiki/en/Ranking_criteria/osu!catch
// the one case wherein that breaks stable is on some marathon maps;
// on those setting Overall Difficulty too high can lead to score V1 exceeding 32 bits ("score overflow").
// that case can be manually handled by mappers.
Beatmap.Difficulty.OverallDifficulty = approachRateSlider.Current.Value;
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
Beatmap.Difficulty.SliderTickRate = tickRateSlider.Current.Value;
@@ -175,8 +175,7 @@ namespace osu.Game.Rulesets.Catch.Objects
/// </remarks>
public void ConvertFromSliderPath(SliderPath sliderPath, double velocity)
{
var sliderPathVertices = new List<Vector2>();
sliderPath.GetPathToProgress(sliderPathVertices, 0, 1);
var sliderPathVertices = sliderPath.CalculatedPath;
double time = 0;
@@ -19,6 +19,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<ProjectReference Include="..\osu.Game.Tests\osu.Game.Tests.csproj" />
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup>
</Project>
@@ -13,6 +13,7 @@
</Compile>
</ItemGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Tests\osu.Game.Tests.csproj" />
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
</ItemGroup>
@@ -20,10 +20,10 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
[Test]
public void TestKeyCountChange()
{
FormSliderBar<float> keyCount = null!;
FormSliderBar<int> keyCount = null!;
AddStep("go to setup screen", () => InputManager.Key(Key.F4));
AddUntilStep("retrieve key count slider", () => keyCount = Editor.ChildrenOfType<SetupScreen>().Single().ChildrenOfType<FormSliderBar<float>>().First(), () => Is.Not.Null);
AddUntilStep("retrieve key count slider", () => keyCount = Editor.ChildrenOfType<SetupScreen>().Single().ChildrenOfType<FormSliderBar<int>>().First(), () => Is.Not.Null);
AddAssert("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
AddStep("change key count to 8", () =>
{
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
});
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().SingleOrDefault()?.CurrentDialog, Is.InstanceOf<SaveAndReloadEditorDialog>);
AddStep("refuse", () => InputManager.Key(Key.Number2));
AddAssert("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
AddUntilStep("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
AddStep("change key count to 8 again", () =>
{
@@ -41,5 +41,32 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
AddStep("acquiesce", () => InputManager.Key(Key.Number1));
AddUntilStep("beatmap became 8K", () => Game.Beatmap.Value.BeatmapInfo.Difficulty.CircleSize, () => Is.EqualTo(8));
}
[Test]
public void TestDualStagesChange()
{
FormCheckBox dualStages = null!;
FormSliderBar<int> keyCount = null!;
AddStep("go to setup screen", () => InputManager.Key(Key.F4));
AddUntilStep("retrieve dual stages checkbox", () => dualStages = Editor.ChildrenOfType<SetupScreen>().Single().ChildrenOfType<FormCheckBox>().First(), () => Is.Not.Null);
AddUntilStep("retrieve key count slider", () => keyCount = Editor.ChildrenOfType<SetupScreen>().Single().ChildrenOfType<FormSliderBar<int>>().First(), () => Is.Not.Null);
AddAssert("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
AddStep("set dual stages", () =>
{
dualStages.Current.Value = true;
});
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().SingleOrDefault()?.CurrentDialog, Is.InstanceOf<SaveAndReloadEditorDialog>);
AddStep("refuse", () => InputManager.Key(Key.Number2));
AddUntilStep("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
AddStep("set dual stages again", () =>
{
dualStages.Current.Value = true;
});
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().Single().CurrentDialog, Is.InstanceOf<SaveAndReloadEditorDialog>);
AddStep("acquiesce", () => InputManager.Key(Key.Number1));
AddUntilStep("beatmap became 12K", () => Game.Beatmap.Value.BeatmapInfo.Difficulty.CircleSize, () => Is.EqualTo(12));
}
}
}
@@ -57,6 +57,28 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
AddAssert("time is unchanged", () => EditorClock.CurrentTime, () => Is.EqualTo(initialTime));
}
[Test]
public void TestNoTwoObjectsAtSameTimeAndColumn()
{
AddStep("change seek setting to false", () => config.SetValue(OsuSetting.EditorAutoSeekOnPlacement, false));
AddStep("clear beatmap", () => EditorBeatmap.Clear());
AddStep("select note placement tool", () => InputManager.Key(Key.Number2));
AddStep("move mouse to centre of last column", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Last().ScreenSpaceDrawQuad.Centre));
AddStep("place note", () => InputManager.Click(MouseButton.Left));
AddAssert("beatmap has 1 object", () => EditorBeatmap.HitObjects, () => Has.Count.EqualTo(1));
AddStep("select note placement tool", () => InputManager.Key(Key.Number2));
AddStep("move mouse to centre of first column", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().First().ScreenSpaceDrawQuad.Centre));
AddStep("place note", () => InputManager.Click(MouseButton.Left));
AddAssert("beatmap has 2 objects", () => EditorBeatmap.HitObjects, () => Has.Count.EqualTo(2));
AddStep("select note placement tool", () => InputManager.Key(Key.Number2));
AddStep("move mouse to centre of last column", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Last().ScreenSpaceDrawQuad.Centre));
AddStep("place note", () => InputManager.Click(MouseButton.Left));
AddAssert("beatmap has 2 objects", () => EditorBeatmap.HitObjects, () => Has.Count.EqualTo(2));
}
private void placeObject()
{
AddStep("select note placement tool", () => InputManager.Key(Key.Number2));
@@ -27,6 +27,24 @@ namespace osu.Game.Rulesets.Mania.Tests
[TestCase("100374")]
[TestCase("1450162")]
[TestCase("4869637")]
[TestCase("1K")]
[TestCase("2K")]
[TestCase("3K")]
[TestCase("4K")]
[TestCase("5K")]
[TestCase("6K")]
[TestCase("7K")]
[TestCase("8K")]
[TestCase("9K")]
[TestCase("10K")]
// [TestCase("11K")] <- See comment in `ManiaBeatmapConverter` ctor for disable reason.
[TestCase("12K")]
// [TestCase("13K")] <- See comment in `ManiaBeatmapConverter` ctor for disable reason.
[TestCase("14K")]
// [TestCase("15K")] <- See comment in `ManiaBeatmapConverter` ctor for disable reason.
[TestCase("16K")]
// [TestCase("17K")] <- See comment in `ManiaBeatmapConverter` ctor for disable reason.
[TestCase("18K")]
public void Test(string name) => base.Test(name);
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
@@ -0,0 +1,45 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.IO.Stores;
using static osu.Game.Tests.Beatmaps.Formats.LegacyBeatmapEncoderTest;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class ManiaLegacyBeatmapEncoderTest
{
private static readonly DllResourceStore beatmaps_resource_store = new DllResourceStore(typeof(ManiaLegacyBeatmapEncoderTest).Assembly);
[TestCase("1K")]
[TestCase("2K")]
[TestCase("3K")]
[TestCase("4K")]
[TestCase("5K")]
[TestCase("6K")]
[TestCase("7K")]
[TestCase("8K")]
[TestCase("9K")]
[TestCase("10K")]
// [TestCase("11K")] <- See comment in `ManiaBeatmapConverter` ctor for disable reason.
[TestCase("12K")]
// [TestCase("13K")] <- See comment in `ManiaBeatmapConverter` ctor for disable reason.
[TestCase("14K")]
// [TestCase("15K")] <- See comment in `ManiaBeatmapConverter` ctor for disable reason.
[TestCase("16K")]
// [TestCase("17K")] <- See comment in `ManiaBeatmapConverter` ctor for disable reason.
[TestCase("18K")]
[TestCase("7K+1")]
public void TestEncodeDecodeStability(string name)
{
var decoded = DecodeFromLegacy(beatmaps_resource_store.GetStream($"Resources/Testing/Beatmaps/{name}.osu"), beatmaps_resource_store, name);
var decodedAfterEncode = DecodeFromLegacy(EncodeToLegacy(decoded), beatmaps_resource_store, name);
Sort(decoded.beatmap);
Sort(decodedAfterEncode.beatmap);
CompareBeatmaps(decoded, decodedAfterEncode);
}
}
}
@@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
{
public partial class TestSceneManiaModNoRelease : RateAdjustedBeatmapTestScene
{
protected override Ruleset CreateRuleset() => new ManiaRuleset();
private const double time_before_head = 250;
private const double time_head = 1500;
private const double time_during_hold_1 = 2500;
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]},{"RandomW":273326509,"RandomX":511,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1125.0,"Objects":[{"StartTime":1125.0,"EndTime":1125.0,"Column":9}]}]}
@@ -0,0 +1,46 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:10K
[Difficulty]
HPDrainRate:5
CircleSize:10
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
25,192,0,1,0,0:0:0:0:
76,192,125,1,0,0:0:0:0:
128,192,250,1,0,0:0:0:0:
179,192,375,1,0,0:0:0:0:
230,192,500,1,0,0:0:0:0:
281,192,625,1,0,0:0:0:0:
332,192,750,1,0,0:0:0:0:
384,192,875,1,0,0:0:0:0:
435,192,1000,1,0,0:0:0:0:
486,192,1125,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1125.0,"Objects":[{"StartTime":1125.0,"EndTime":1125.0,"Column":9}]},{"RandomW":273326509,"RandomX":531,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1250.0,"Objects":[{"StartTime":1250.0,"EndTime":1250.0,"Column":10}]}]}
@@ -0,0 +1,47 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:11K
[Difficulty]
HPDrainRate:5
CircleSize:11
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
23,192,0,1,0,0:0:0:0:
69,192,125,1,0,0:0:0:0:
116,192,250,1,0,0:0:0:0:
162,192,375,1,0,0:0:0:0:
209,192,500,1,0,0:0:0:0:
256,192,625,1,0,0:0:0:0:
302,192,750,1,0,0:0:0:0:
349,192,875,1,0,0:0:0:0:
395,192,1000,1,0,0:0:0:0:
442,192,1125,1,0,0:0:0:0:
488,192,1250,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1125.0,"Objects":[{"StartTime":1125.0,"EndTime":1125.0,"Column":9}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1250.0,"Objects":[{"StartTime":1250.0,"EndTime":1250.0,"Column":10}]},{"RandomW":273326509,"RandomX":551,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1375.0,"Objects":[{"StartTime":1375.0,"EndTime":1375.0,"Column":11}]}]}
@@ -0,0 +1,48 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:12K
[Difficulty]
HPDrainRate:5
CircleSize:12
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
21,192,0,1,0,0:0:0:0:
64,192,125,1,0,0:0:0:0:
106,192,250,1,0,0:0:0:0:
149,192,375,1,0,0:0:0:0:
192,192,500,1,0,0:0:0:0:
234,192,625,1,0,0:0:0:0:
277,192,750,1,0,0:0:0:0:
320,192,875,1,0,0:0:0:0:
362,192,1000,1,0,0:0:0:0:
405,192,1125,1,0,0:0:0:0:
448,192,1250,1,0,0:0:0:0:
490,192,1375,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1125.0,"Objects":[{"StartTime":1125.0,"EndTime":1125.0,"Column":9}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1250.0,"Objects":[{"StartTime":1250.0,"EndTime":1250.0,"Column":10}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1375.0,"Objects":[{"StartTime":1375.0,"EndTime":1375.0,"Column":11}]},{"RandomW":273326509,"RandomX":571,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1500.0,"Objects":[{"StartTime":1500.0,"EndTime":1500.0,"Column":12}]}]}
@@ -0,0 +1,49 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:13K
[Difficulty]
HPDrainRate:5
CircleSize:13
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
19,192,0,1,0,0:0:0:0:
59,192,125,1,0,0:0:0:0:
98,192,250,1,0,0:0:0:0:
137,192,375,1,0,0:0:0:0:
177,192,500,1,0,0:0:0:0:
216,192,625,1,0,0:0:0:0:
256,192,750,1,0,0:0:0:0:
295,192,875,1,0,0:0:0:0:
334,192,1000,1,0,0:0:0:0:
374,192,1125,1,0,0:0:0:0:
413,192,1250,1,0,0:0:0:0:
452,192,1375,1,0,0:0:0:0:
492,192,1500,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1125.0,"Objects":[{"StartTime":1125.0,"EndTime":1125.0,"Column":9}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1250.0,"Objects":[{"StartTime":1250.0,"EndTime":1250.0,"Column":10}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1375.0,"Objects":[{"StartTime":1375.0,"EndTime":1375.0,"Column":11}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1500.0,"Objects":[{"StartTime":1500.0,"EndTime":1500.0,"Column":12}]},{"RandomW":273326509,"RandomX":591,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1625.0,"Objects":[{"StartTime":1625.0,"EndTime":1625.0,"Column":13}]}]}
@@ -0,0 +1,50 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:14K
[Difficulty]
HPDrainRate:5
CircleSize:14
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
18,192,0,1,0,0:0:0:0:
54,192,125,1,0,0:0:0:0:
91,192,250,1,0,0:0:0:0:
128,192,375,1,0,0:0:0:0:
164,192,500,1,0,0:0:0:0:
201,192,625,1,0,0:0:0:0:
237,192,750,1,0,0:0:0:0:
274,192,875,1,0,0:0:0:0:
310,192,1000,1,0,0:0:0:0:
347,192,1125,1,0,0:0:0:0:
384,192,1250,1,0,0:0:0:0:
420,192,1375,1,0,0:0:0:0:
457,192,1500,1,0,0:0:0:0:
493,192,1625,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1125.0,"Objects":[{"StartTime":1125.0,"EndTime":1125.0,"Column":9}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1250.0,"Objects":[{"StartTime":1250.0,"EndTime":1250.0,"Column":10}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1375.0,"Objects":[{"StartTime":1375.0,"EndTime":1375.0,"Column":11}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1500.0,"Objects":[{"StartTime":1500.0,"EndTime":1500.0,"Column":12}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1625.0,"Objects":[{"StartTime":1625.0,"EndTime":1625.0,"Column":13}]},{"RandomW":273326509,"RandomX":611,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1750.0,"Objects":[{"StartTime":1750.0,"EndTime":1750.0,"Column":14}]}]}
@@ -0,0 +1,51 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:15K
[Difficulty]
HPDrainRate:5
CircleSize:15
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
17,192,0,1,0,0:0:0:0:
51,192,125,1,0,0:0:0:0:
85,192,250,1,0,0:0:0:0:
119,192,375,1,0,0:0:0:0:
153,192,500,1,0,0:0:0:0:
187,192,625,1,0,0:0:0:0:
221,192,750,1,0,0:0:0:0:
256,192,875,1,0,0:0:0:0:
290,192,1000,1,0,0:0:0:0:
324,192,1125,1,0,0:0:0:0:
358,192,1250,1,0,0:0:0:0:
392,192,1375,1,0,0:0:0:0:
426,192,1500,1,0,0:0:0:0:
460,192,1625,1,0,0:0:0:0:
494,192,1750,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1125.0,"Objects":[{"StartTime":1125.0,"EndTime":1125.0,"Column":9}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1250.0,"Objects":[{"StartTime":1250.0,"EndTime":1250.0,"Column":10}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1375.0,"Objects":[{"StartTime":1375.0,"EndTime":1375.0,"Column":11}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1500.0,"Objects":[{"StartTime":1500.0,"EndTime":1500.0,"Column":12}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1625.0,"Objects":[{"StartTime":1625.0,"EndTime":1625.0,"Column":13}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1750.0,"Objects":[{"StartTime":1750.0,"EndTime":1750.0,"Column":14}]},{"RandomW":273326509,"RandomX":631,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1875.0,"Objects":[{"StartTime":1875.0,"EndTime":1875.0,"Column":15}]}]}
@@ -0,0 +1,52 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:16K
[Difficulty]
HPDrainRate:5
CircleSize:16
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
16,192,0,1,0,0:0:0:0:
48,192,125,1,0,0:0:0:0:
80,192,250,1,0,0:0:0:0:
112,192,375,1,0,0:0:0:0:
144,192,500,1,0,0:0:0:0:
176,192,625,1,0,0:0:0:0:
208,192,750,1,0,0:0:0:0:
240,192,875,1,0,0:0:0:0:
272,192,1000,1,0,0:0:0:0:
304,192,1125,1,0,0:0:0:0:
336,192,1250,1,0,0:0:0:0:
368,192,1375,1,0,0:0:0:0:
400,192,1500,1,0,0:0:0:0:
432,192,1625,1,0,0:0:0:0:
464,192,1750,1,0,0:0:0:0:
496,192,1875,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1125.0,"Objects":[{"StartTime":1125.0,"EndTime":1125.0,"Column":9}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1250.0,"Objects":[{"StartTime":1250.0,"EndTime":1250.0,"Column":10}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1375.0,"Objects":[{"StartTime":1375.0,"EndTime":1375.0,"Column":11}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1500.0,"Objects":[{"StartTime":1500.0,"EndTime":1500.0,"Column":12}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1625.0,"Objects":[{"StartTime":1625.0,"EndTime":1625.0,"Column":13}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1750.0,"Objects":[{"StartTime":1750.0,"EndTime":1750.0,"Column":14}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1875.0,"Objects":[{"StartTime":1875.0,"EndTime":1875.0,"Column":15}]},{"RandomW":273326509,"RandomX":651,"RandomY":842502087,"RandomZ":3579807591,"StartTime":2000.0,"Objects":[{"StartTime":2000.0,"EndTime":2000.0,"Column":16}]}]}
@@ -0,0 +1,53 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:17K
[Difficulty]
HPDrainRate:5
CircleSize:17
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
15,192,0,1,0,0:0:0:0:
45,192,125,1,0,0:0:0:0:
75,192,250,1,0,0:0:0:0:
105,192,375,1,0,0:0:0:0:
135,192,500,1,0,0:0:0:0:
165,192,625,1,0,0:0:0:0:
195,192,750,1,0,0:0:0:0:
225,192,875,1,0,0:0:0:0:
256,192,1000,1,0,0:0:0:0:
286,192,1125,1,0,0:0:0:0:
316,192,1250,1,0,0:0:0:0:
346,192,1375,1,0,0:0:0:0:
376,192,1500,1,0,0:0:0:0:
406,192,1625,1,0,0:0:0:0:
436,192,1750,1,0,0:0:0:0:
466,192,1875,1,0,0:0:0:0:
496,192,2000,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1125.0,"Objects":[{"StartTime":1125.0,"EndTime":1125.0,"Column":9}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1250.0,"Objects":[{"StartTime":1250.0,"EndTime":1250.0,"Column":10}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1375.0,"Objects":[{"StartTime":1375.0,"EndTime":1375.0,"Column":11}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1500.0,"Objects":[{"StartTime":1500.0,"EndTime":1500.0,"Column":12}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1625.0,"Objects":[{"StartTime":1625.0,"EndTime":1625.0,"Column":13}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1750.0,"Objects":[{"StartTime":1750.0,"EndTime":1750.0,"Column":14}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1875.0,"Objects":[{"StartTime":1875.0,"EndTime":1875.0,"Column":15}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":2000.0,"Objects":[{"StartTime":2000.0,"EndTime":2000.0,"Column":16}]},{"RandomW":273326509,"RandomX":671,"RandomY":842502087,"RandomZ":3579807591,"StartTime":2125.0,"Objects":[{"StartTime":2125.0,"EndTime":2125.0,"Column":17}]}]}
@@ -0,0 +1,54 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:18K
[Difficulty]
HPDrainRate:5
CircleSize:18
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
14,192,0,1,0,0:0:0:0:
42,192,125,1,0,0:0:0:0:
71,192,250,1,0,0:0:0:0:
99,192,375,1,0,0:0:0:0:
128,192,500,1,0,0:0:0:0:
156,192,625,1,0,0:0:0:0:
184,192,750,1,0,0:0:0:0:
213,192,875,1,0,0:0:0:0:
241,192,1000,1,0,0:0:0:0:
270,192,1125,1,0,0:0:0:0:
298,192,1250,1,0,0:0:0:0:
327,192,1375,1,0,0:0:0:0:
355,192,1500,1,0,0:0:0:0:
384,192,1625,1,0,0:0:0:0:
412,192,1750,1,0,0:0:0:0:
440,192,1875,1,0,0:0:0:0:
469,192,2000,1,0,0:0:0:0:
497,192,2125,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":331,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]}]}
@@ -0,0 +1,37 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:1K
[Difficulty]
HPDrainRate:5
CircleSize:1
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
256,192,0,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":351,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":351,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]}]}
@@ -0,0 +1,38 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:2K
[Difficulty]
HPDrainRate:5
CircleSize:2
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
128,192,0,1,0,0:0:0:0:
384,192,125,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":371,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":371,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":371,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]}]}
@@ -0,0 +1,39 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:3K
[Difficulty]
HPDrainRate:5
CircleSize:3
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
85,192,0,1,0,0:0:0:0:
256,192,125,1,0,0:0:0:0:
426,192,250,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":391,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":391,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":391,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":391,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]}]}
@@ -0,0 +1,40 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:4K
[Difficulty]
HPDrainRate:5
CircleSize:4
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
64,192,0,1,0,0:0:0:0:
192,192,125,1,0,0:0:0:0:
320,192,250,1,0,0:0:0:0:
448,192,375,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":411,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":411,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":411,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":411,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":411,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]}]}
@@ -0,0 +1,41 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:5K
[Difficulty]
HPDrainRate:5
CircleSize:5
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
51,192,0,1,0,0:0:0:0:
153,192,125,1,0,0:0:0:0:
256,192,250,1,0,0:0:0:0:
358,192,375,1,0,0:0:0:0:
460,192,500,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":431,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":431,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":431,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":431,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":431,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":431,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]}]}
@@ -0,0 +1,42 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:6K
[Difficulty]
HPDrainRate:5
CircleSize:6
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
42,192,0,1,0,0:0:0:0:
128,192,125,1,0,0:0:0:0:
213,192,250,1,0,0:0:0:0:
298,192,375,1,0,0:0:0:0:
384,192,500,1,0,0:0:0:0:
469,192,625,1,0,0:0:0:0:
@@ -0,0 +1,45 @@
osu file format v14
[General]
Mode: 3
SpecialStyle:1
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:8K
[Difficulty]
HPDrainRate:5
CircleSize:8
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
32,192,0,1,0,0:0:0:0:
96,192,125,1,0,0:0:0:0:
160,192,250,1,0,0:0:0:0:
224,192,375,1,0,0:0:0:0:
288,192,500,1,0,0:0:0:0:
352,192,625,1,0,0:0:0:0:
416,192,750,1,0,0:0:0:0:
480,192,875,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":451,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":451,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":451,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":451,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":451,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":451,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":451,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]}]}
@@ -0,0 +1,43 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:7K
[Difficulty]
HPDrainRate:5
CircleSize:7
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
36,192,0,1,0,0:0:0:0:
109,192,125,1,0,0:0:0:0:
182,192,250,1,0,0:0:0:0:
256,192,375,1,0,0:0:0:0:
329,192,500,1,0,0:0:0:0:
402,192,625,1,0,0:0:0:0:
475,192,750,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":471,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":471,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":471,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":471,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":471,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":471,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":471,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":471,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]}]}
@@ -0,0 +1,44 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:8K
[Difficulty]
HPDrainRate:5
CircleSize:8
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
32,192,0,1,0,0:0:0:0:
96,192,125,1,0,0:0:0:0:
160,192,250,1,0,0:0:0:0:
224,192,375,1,0,0:0:0:0:
288,192,500,1,0,0:0:0:0:
352,192,625,1,0,0:0:0:0:
416,192,750,1,0,0:0:0:0:
480,192,875,1,0,0:0:0:0:
@@ -0,0 +1 @@
{"Mappings":[{"RandomW":273326509,"RandomX":491,"RandomY":842502087,"RandomZ":3579807591,"StartTime":0.0,"Objects":[{"StartTime":0.0,"EndTime":0.0,"Column":0}]},{"RandomW":273326509,"RandomX":491,"RandomY":842502087,"RandomZ":3579807591,"StartTime":125.0,"Objects":[{"StartTime":125.0,"EndTime":125.0,"Column":1}]},{"RandomW":273326509,"RandomX":491,"RandomY":842502087,"RandomZ":3579807591,"StartTime":250.0,"Objects":[{"StartTime":250.0,"EndTime":250.0,"Column":2}]},{"RandomW":273326509,"RandomX":491,"RandomY":842502087,"RandomZ":3579807591,"StartTime":375.0,"Objects":[{"StartTime":375.0,"EndTime":375.0,"Column":3}]},{"RandomW":273326509,"RandomX":491,"RandomY":842502087,"RandomZ":3579807591,"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"Column":4}]},{"RandomW":273326509,"RandomX":491,"RandomY":842502087,"RandomZ":3579807591,"StartTime":625.0,"Objects":[{"StartTime":625.0,"EndTime":625.0,"Column":5}]},{"RandomW":273326509,"RandomX":491,"RandomY":842502087,"RandomZ":3579807591,"StartTime":750.0,"Objects":[{"StartTime":750.0,"EndTime":750.0,"Column":6}]},{"RandomW":273326509,"RandomX":491,"RandomY":842502087,"RandomZ":3579807591,"StartTime":875.0,"Objects":[{"StartTime":875.0,"EndTime":875.0,"Column":7}]},{"RandomW":273326509,"RandomX":491,"RandomY":842502087,"RandomZ":3579807591,"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"Column":8}]}]}
@@ -0,0 +1,45 @@
osu file format v14
[General]
Mode: 3
[Metadata]
Title:keycount test
TitleUnicode:keycount test
Artist:mania
ArtistUnicode:mania
Creator:spaceman_atlas
Version:9K
[Difficulty]
HPDrainRate:5
CircleSize:9
OverallDifficulty:5
ApproachRate:5
SliderMultiplier:1.4
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 Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
0,500,4,2,0,100,1,0
[HitObjects]
28,192,0,1,0,0:0:0:0:
85,192,125,1,0,0:0:0:0:
142,192,250,1,0,0:0:0:0:
199,192,375,1,0,0:0:0:0:
256,192,500,1,0,0:0:0:0:
312,192,625,1,0,0:0:0:0:
369,192,750,1,0,0:0:0:0:
426,192,875,1,0,0:0:0:0:
483,192,1000,1,0,0:0:0:0:
@@ -33,6 +33,8 @@ namespace osu.Game.Rulesets.Mania.Tests
/// </summary>
public partial class TestSceneHoldNoteInput : RateAdjustedBeatmapTestScene
{
protected override Ruleset CreateRuleset() => new ManiaRuleset();
private const double time_before_head = 250;
private const double time_head = 1500;
private const double time_during_hold_1 = 2500;
@@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Mania.Tests
{
public partial class TestSceneMaximumScore : RateAdjustedBeatmapTestScene
{
protected override Ruleset CreateRuleset() => new ManiaRuleset();
private ScoreAccessibleReplayPlayer currentPlayer = null!;
private List<JudgementResult> judgementResults = new List<JudgementResult>();
@@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Mania.Tests
{
public partial class TestSceneOutOfOrderHits : RateAdjustedBeatmapTestScene
{
protected override Ruleset CreateRuleset() => new ManiaRuleset();
[Test]
public void TestPreviousHitWindowDoesNotExtendPastNextObject()
{
@@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Mania.Tests
{
public partial class TestSceneReplayRewinding : RateAdjustedBeatmapTestScene
{
protected override Ruleset CreateRuleset() => new ManiaRuleset();
private ReplayPlayer currentPlayer = null!;
[Test]
@@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Mania.Tests
[TestFixture]
public partial class TestSceneTimingBasedNoteColouring : OsuTestScene
{
protected override Ruleset CreateRuleset() => new ManiaRuleset();
private Bindable<bool> configTimingBasedNoteColouring;
private ManualClock clock;
@@ -11,5 +11,6 @@
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<ProjectReference Include="..\osu.Game.Tests\osu.Game.Tests.csproj" />
</ItemGroup>
</Project>
@@ -66,6 +66,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
if (IsForCurrentRuleset && TargetColumns > ManiaRuleset.MAX_STAGE_KEYS)
{
// If support for odd key counts above 10 (11 / 13 / 15 / 17K) is ever desired, this code needs to go.
// For now, it's probably fine, as stable doesn't support them outside of manual .osu edits either.
TargetColumns /= 2;
Dual = true;
}
@@ -7,6 +7,7 @@ using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Localisation;
using osu.Game.Resources.Localisation.Web;
@@ -19,13 +20,22 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
{
public override LocalisableString Title => EditorSetupStrings.DifficultyHeader;
private FormSliderBar<float> keyCountSlider { get; set; } = null!;
private FormSliderBar<int> keyCountSlider { get; set; } = null!;
private FormCheckBox dualStages { get; set; } = null!;
private FormCheckBox specialStyle { get; set; } = null!;
private FormSliderBar<float> healthDrainSlider { get; set; } = null!;
private FormSliderBar<float> overallDifficultySlider { get; set; } = null!;
private FormSliderBar<double> baseVelocitySlider { get; set; } = null!;
private FormSliderBar<double> tickRateSlider { get; set; } = null!;
private readonly BindableInt singleStageKeyCount = new BindableInt
{
Default = (int)BeatmapDifficulty.DEFAULT_DIFFICULTY,
Precision = 1,
};
private readonly BindableInt actualKeyCount = new BindableInt();
[Resolved]
private Editor? editor { get; set; }
@@ -37,20 +47,19 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
{
Children = new Drawable[]
{
keyCountSlider = new FormSliderBar<float>
keyCountSlider = new FormSliderBar<int>
{
Caption = BeatmapsetsStrings.ShowStatsCsMania,
HintText = "The number of columns in the beatmap",
Current = new BindableFloat(Beatmap.Difficulty.CircleSize)
{
Default = BeatmapDifficulty.DEFAULT_DIFFICULTY,
MinValue = 0,
MaxValue = 10,
Precision = 1,
},
Current = singleStageKeyCount,
TransferValueOnCommit = true,
TabbableContentContainer = this,
},
dualStages = new FormCheckBox
{
Caption = "Dual stages",
HintText = "Doubles the number of keys by adding a second stage."
},
specialStyle = new FormCheckBox
{
Caption = "Use special (N+1) style",
@@ -117,16 +126,54 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
},
};
keyCountSlider.Current.BindValueChanged(updateKeyCount);
setStateFromActualKeyCount((int)Beatmap.Difficulty.CircleSize);
keyCountSlider.Current.BindValueChanged(_ => calculateActualKeyCount());
dualStages.Current.BindValueChanged(_ =>
{
updateSingleStageKeyCountBounds();
calculateActualKeyCount();
});
actualKeyCount.BindValueChanged(updateKeyCount);
healthDrainSlider.Current.BindValueChanged(_ => updateValues());
overallDifficultySlider.Current.BindValueChanged(_ => updateValues());
baseVelocitySlider.Current.BindValueChanged(_ => updateValues());
tickRateSlider.Current.BindValueChanged(_ => updateValues());
}
private void updateSingleStageKeyCountBounds()
{
singleStageKeyCount.MinValue = dualStages.Current.Value ? ManiaRuleset.MAX_STAGE_KEYS / 2 + 1 : 1;
singleStageKeyCount.MaxValue = dualStages.Current.Value ? LegacyBeatmapDecoder.MAX_MANIA_KEY_COUNT / 2 : ManiaRuleset.MAX_STAGE_KEYS;
}
private void setStateFromActualKeyCount(int keyCount)
{
actualKeyCount.Value = keyCount;
if (keyCount > 10)
{
dualStages.Current.Value = true;
singleStageKeyCount.Value = keyCount / 2;
}
else
{
dualStages.Current.Value = false;
singleStageKeyCount.Value = keyCount;
}
updateSingleStageKeyCountBounds();
}
private void calculateActualKeyCount()
{
actualKeyCount.Value = keyCountSlider.Current.Value * (dualStages.Current.Value ? 2 : 1);
}
private bool updatingKeyCount;
private void updateKeyCount(ValueChangedEvent<float> keyCount)
private void updateKeyCount(ValueChangedEvent<int> keyCount)
{
if (updatingKeyCount) return;
@@ -143,7 +190,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
Schedule(() =>
{
changeHandler!.RestoreState(-1);
Beatmap.Difficulty.CircleSize = keyCountSlider.Current.Value = keyCount.OldValue;
Beatmap.Difficulty.CircleSize = keyCount.OldValue;
setStateFromActualKeyCount(keyCount.OldValue);
updatingKeyCount = false;
});
}
@@ -158,7 +206,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
{
// for now, update these on commit rather than making BeatmapMetadata bindables.
// after switching database engines we can reconsider if switching to bindables is a good direction.
Beatmap.Difficulty.CircleSize = keyCountSlider.Current.Value;
Beatmap.Difficulty.CircleSize = actualKeyCount.Value;
Beatmap.SpecialStyle = specialStyle.Current.Value;
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
@@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Mania
return false;
}
public bool FilterMayChangeFromMods(ValueChangedEvent<IReadOnlyList<Mod>> mods)
public bool FilterMayChangeFromMods(FilterCriteria criteria, ValueChangedEvent<IReadOnlyList<Mod>> mods)
{
if (includedKeyCounts.Count != LegacyBeatmapDecoder.MAX_MANIA_KEY_COUNT)
if (includedKeyCounts.Count != LegacyBeatmapDecoder.MAX_MANIA_KEY_COUNT || criteria.Group == GroupMode.Variant)
{
// Interpreting as the Mod type is required for equality comparison.
HashSet<Mod> oldSet = mods.OldValue.OfType<ManiaKeyMod>().AsEnumerable<Mod>().ToHashSet();
+21
View File
@@ -327,6 +327,8 @@ namespace osu.Game.Rulesets.Mania
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
public override LocalisableString VariantDescription => "Keys";
public override IEnumerable<int> AvailableVariants
{
get
@@ -483,6 +485,22 @@ namespace osu.Game.Rulesets.Mania
};
}
public override IEnumerable<RulesetBeatmapAttribute> GetBeatmapAttributesForRankedPlayCard(IBeatmapInfo beatmapInfo, IReadOnlyCollection<Mod> mods)
{
var attributes = GetBeatmapAttributesForDisplay(beatmapInfo, mods).ToList();
// Key count attribute isn't relevant to ranked play (it's decided by the pool).
attributes.RemoveAll(a => a.Acronym == "KC");
float holdNoteRatio = beatmapInfo.TotalObjectCount == 0 ? 0 : (float)beatmapInfo.EndTimeObjectCount / beatmapInfo.TotalObjectCount;
attributes.Insert(0, new RulesetBeatmapAttribute("Hold notes", @"HN", holdNoteRatio, holdNoteRatio, 1)
{
ValueFormat = "P0"
});
return attributes;
}
public override IRulesetFilterCriteria CreateRulesetFilterCriteria()
{
return new ManiaFilterCriteria();
@@ -498,6 +516,9 @@ namespace osu.Game.Rulesets.Mania
public int GetKeyCount(IBeatmapInfo beatmapInfo, IReadOnlyList<Mod>? mods = null)
=> ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), mods);
public override int GetVariantForBeatmap(IBeatmapInfo beatmapInfo, IReadOnlyList<Mod>? mods = null)
=> GetKeyCount(beatmapInfo, mods);
}
public enum PlayfieldType
@@ -73,6 +73,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("end slider placement", () => InputManager.Click(MouseButton.Right));
AddStep("seek to slider end", () =>
{
var slider = (Slider)EditorBeatmap.HitObjects.Single();
EditorClock.Seek(slider.EndTime);
});
AddStep("enter circle placement mode", () => InputManager.Key(Key.Number2));
AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.205f, 0)));
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
@@ -57,11 +56,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
if (lastVersion != hitObject.Path.Version.Value)
{
lastVersion = hitObject.Path.Version.Value;
var vertices = new List<Vector2>();
hitObject.Path.GetPathToProgress(vertices, 0, 1);
body.SetVertices(vertices);
body.SetVertices(hitObject.Path.CalculatedPath);
}
OriginPosition = body.PathOffset;
@@ -0,0 +1,14 @@
// 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.Edit.Checks;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Edit.Checks
{
public class CheckOsuFewHitsounds : CheckFewHitsounds
{
protected override bool IsExcludedFromHitsounding(HitObject hitObject) => hitObject is Spinner;
}
}
@@ -3,6 +3,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Blueprints;
@@ -22,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Edit
""";
}
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.DraftingCompass };
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.EditorGrid };
public override PlacementBlueprint CreatePlacementBlueprint() => new GridPlacementBlueprint();
}
@@ -2,7 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
@@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles);
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.EditorHitCircle };
public override HitObjectPlacementBlueprint CreatePlacementBlueprint() => new HitCirclePlacementBlueprint();
}
@@ -14,6 +14,9 @@ namespace osu.Game.Rulesets.Osu.Edit
{
private readonly List<ICheck> checks = new List<ICheck>
{
// Audio
new CheckOsuFewHitsounds(),
// Compose
new CheckOffscreenObjects(),
new CheckTooShortSpinners(),
@@ -125,13 +125,23 @@ namespace osu.Game.Rulesets.Osu.Edit
{
startPositionXSlider = new ExpandableSlider<float>
{
Current = StartPositionX,
Current = new BindableFloat
{
MinValue = -OsuPlayfield.BASE_SIZE.X / 2,
MaxValue = OsuPlayfield.BASE_SIZE.X / 2,
Precision = 0.1f,
},
KeyboardStep = 1,
ExpandedLabelText = "X offset",
},
startPositionYSlider = new ExpandableSlider<float>
{
Current = StartPositionY,
Current = new BindableFloat
{
MinValue = -OsuPlayfield.BASE_SIZE.Y / 2,
MaxValue = OsuPlayfield.BASE_SIZE.Y / 2,
Precision = 0.1f,
},
KeyboardStep = 1,
ExpandedLabelText = "Y offset",
},
@@ -186,15 +196,27 @@ namespace osu.Game.Rulesets.Osu.Edit
StartPositionX.BindValueChanged(x =>
{
startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:#,0.##}";
startPositionXSlider.Current.Value = x.NewValue - OsuPlayfield.BASE_SIZE.X / 2;
StartPosition.Value = new Vector2(x.NewValue, StartPosition.Value.Y);
}, true);
StartPositionY.BindValueChanged(y =>
{
startPositionYSlider.ContractedLabelText = $"Y: {y.NewValue:#,0.##}";
startPositionYSlider.Current.Value = y.NewValue - OsuPlayfield.BASE_SIZE.Y / 2;
StartPosition.Value = new Vector2(StartPosition.Value.X, y.NewValue);
}, true);
startPositionXSlider.Current.BindValueChanged(x =>
{
StartPositionX.Value = x.NewValue + OsuPlayfield.BASE_SIZE.X / 2;
});
startPositionYSlider.Current.BindValueChanged(y =>
{
StartPositionY.Value = y.NewValue + OsuPlayfield.BASE_SIZE.Y / 2;
});
StartPosition.BindValueChanged(pos =>
{
StartPositionX.Value = pos.NewValue.X;
@@ -2,7 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
@@ -24,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Edit
""";
}
public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders);
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.EditorSlider };
public override HitObjectPlacementBlueprint CreatePlacementBlueprint() => new SliderPlacementBlueprint();
}
@@ -2,7 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
@@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners);
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.EditorSpinner };
public override HitObjectPlacementBlueprint CreatePlacementBlueprint() => new SpinnerPlacementBlueprint();
}
+1 -1
View File
@@ -21,7 +21,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Mods
{
public partial class OsuModBlinds : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToHealthProcessor
public partial class OsuModBlinds : ModBlinds, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToHealthProcessor
{
public override string Name => "Blinds";
public override LocalisableString Description => "Play with blinds on your screen.";
@@ -16,7 +16,7 @@ using osu.Game.Rulesets.Osu.Skinning.Default;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModTraceable : ModWithVisibilityAdjustment, IRequiresApproachCircles
public class OsuModTraceable : ModTraceable, IRequiresApproachCircles
{
public override string Name => "Traceable";
public override string Acronym => "TC";
@@ -105,7 +105,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
return;
// Generate the entire curve
drawableSlider.HitObject.Path.GetPathToProgress(CurrentCurve, 0, 1);
CurrentCurve.Clear();
CurrentCurve.AddRange(drawableSlider.HitObject.Path.CalculatedPath);
SetVertices(CurrentCurve);
// Force the body to be the final path size to avoid excessive autosize computations
@@ -265,9 +265,6 @@ namespace osu.Game.Rulesets.Osu.Utils
/// </remarks>
public static RectangleF CalculatePossibleMovementBounds(Slider slider)
{
var pathPositions = new List<Vector2>();
slider.Path.GetPathToProgress(pathPositions, 0, 1);
float minX = float.PositiveInfinity;
float maxX = float.NegativeInfinity;
@@ -275,7 +272,7 @@ namespace osu.Game.Rulesets.Osu.Utils
float maxY = float.NegativeInfinity;
// Compute the bounding box of the slider.
foreach (var pos in pathPositions)
foreach (var pos in slider.Path.CalculatedPath)
{
minX = MathF.Min(minX, pos.X);
maxX = MathF.Max(maxX, pos.X);
@@ -18,11 +18,13 @@ namespace osu.Game.Rulesets.Taiko.Configuration
base.InitialiseDefaults();
SetDefault(TaikoRulesetSetting.TouchControlScheme, TaikoTouchControlScheme.KDDK);
SetDefault(TaikoRulesetSetting.RateAdjustedHitAnimation, true);
}
}
public enum TaikoRulesetSetting
{
TouchControlScheme
TouchControlScheme,
RateAdjustedHitAnimation,
}
}
@@ -7,12 +7,15 @@ using System;
using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Configuration;
using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osuTK;
@@ -34,12 +37,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private set;
}
private bool validActionPressed;
private double? lastPressHandleTime;
[Resolved(CanBeNull = true)]
private TaikoRulesetConfigManager taikoConfig { get; set; }
private readonly Bindable<bool> rateAdjustedHitAnimations = new Bindable<bool>(true);
private readonly Bindable<HitType> type = new Bindable<HitType>();
private bool validActionPressed;
private double? lastPressHandleTime;
public DrawableHit()
: this(null)
{
@@ -51,6 +57,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit;
}
[BackgroundDependencyLoader]
private void load()
{
taikoConfig?.BindWith(TaikoRulesetSetting.RateAdjustedHitAnimation, rateAdjustedHitAnimations);
}
protected override void OnApply()
{
type.BindTo(HitObject.TypeBindable);
@@ -168,11 +180,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (SnapJudgementLocation)
MainPiece.MoveToX(-X);
this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
// Rate independent to match stable.
double rate = Math.Abs((Clock as IGameplayClock)?.GetTrueGameplayRate() ?? Clock.Rate);
double length = gravity_time * (rateAdjustedHitAnimations.Value ? 1 : rate);
this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out)
this.ScaleTo(0.8f, length * 2, Easing.OutQuad);
this.MoveToY(-gravity_travel_height, length, Easing.Out)
.Then()
.MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In);
.MoveToY(gravity_travel_height * 2, length * 2, Easing.In);
this.FadeOut(800);
break;
@@ -3,6 +3,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Localisation;
@@ -31,7 +32,16 @@ namespace osu.Game.Rulesets.Taiko
{
Caption = RulesetSettingsStrings.TouchControlScheme,
Current = config.GetBindable<TaikoTouchControlScheme>(TaikoRulesetSetting.TouchControlScheme)
}),
new SettingsItemV2(new FormCheckBox
{
Caption = RulesetSettingsStrings.RateAdjustedHitAnimation,
HintText = RulesetSettingsStrings.RateAdjustedHitAnimationTooltip,
Current = config.GetBindable<bool>(TaikoRulesetSetting.RateAdjustedHitAnimation)
})
{
ApplyClassicDefault = c => ((IHasCurrentValue<bool>)c).Current.Value = false,
}
};
}
}
@@ -46,11 +46,11 @@ namespace osu.Game.Tests.Beatmaps.Formats
{
const string name = "Resources/storyboard_only_video.osu";
var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name);
var decoded = DecodeFromLegacy(beatmaps_resource_store.GetStream(name), beatmaps_resource_store, name);
Assert.That(decoded.beatmap.UnhandledEventLines.Count, Is.EqualTo(1));
Assert.That(decoded.beatmap.UnhandledEventLines.Single(), Is.EqualTo("Video,0,\"video.avi\""));
var memoryStream = encodeToLegacy(decoded);
var memoryStream = EncodeToLegacy(decoded);
var storyboard = new LegacyStoryboardDecoder().Decode(new LineBufferedReader(memoryStream));
StoryboardLayer video = storyboard.Layers.Single(l => l.Name == "Video");
@@ -60,42 +60,42 @@ namespace osu.Game.Tests.Beatmaps.Formats
[TestCaseSource(nameof(allBeatmaps))]
public void TestEncodeDecodeStability(string name)
{
var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name);
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name);
var decoded = DecodeFromLegacy(beatmaps_resource_store.GetStream(name), beatmaps_resource_store, name);
var decodedAfterEncode = DecodeFromLegacy(EncodeToLegacy(decoded), beatmaps_resource_store, name);
sort(decoded.beatmap);
sort(decodedAfterEncode.beatmap);
Sort(decoded.beatmap);
Sort(decodedAfterEncode.beatmap);
compareBeatmaps(decoded, decodedAfterEncode);
CompareBeatmaps(decoded, decodedAfterEncode);
}
[TestCaseSource(nameof(allBeatmaps))]
public void TestEncodeDecodeStabilityDoubleConvert(string name)
{
var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name);
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name);
var decoded = DecodeFromLegacy(beatmaps_resource_store.GetStream(name), beatmaps_resource_store, name);
var decodedAfterEncode = DecodeFromLegacy(EncodeToLegacy(decoded), beatmaps_resource_store, name);
// run an extra convert. this is expected to be stable.
decodedAfterEncode.beatmap = convert(decodedAfterEncode.beatmap);
sort(decoded.beatmap);
sort(decodedAfterEncode.beatmap);
Sort(decoded.beatmap);
Sort(decodedAfterEncode.beatmap);
compareBeatmaps(decoded, decodedAfterEncode);
CompareBeatmaps(decoded, decodedAfterEncode);
}
[TestCaseSource(nameof(allBeatmaps))]
public void TestEncodeDecodeStabilityWithNonLegacyControlPoints(string name)
{
var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name);
var decoded = DecodeFromLegacy(beatmaps_resource_store.GetStream(name), beatmaps_resource_store, name);
// we are testing that the transfer of relevant data to hitobjects (from legacy control points) sticks through encode/decode.
// before the encode step, the legacy information is removed here.
decoded.beatmap.ControlPointInfo = removeLegacyControlPointTypes(decoded.beatmap.ControlPointInfo);
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name);
var decodedAfterEncode = DecodeFromLegacy(EncodeToLegacy(decoded), beatmaps_resource_store, name);
compareBeatmaps(decoded, decodedAfterEncode);
CompareBeatmaps(decoded, decodedAfterEncode);
static ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo)
{
@@ -120,7 +120,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
private void compareBeatmaps((IBeatmap beatmap, TestLegacySkin skin) expected, (IBeatmap beatmap, TestLegacySkin skin) actual)
public static void CompareBeatmaps((IBeatmap beatmap, TestLegacySkin skin) expected, (IBeatmap beatmap, TestLegacySkin skin) actual)
{
// Check all control points that are still considered to be at a global level.
Assert.That(actual.beatmap.ControlPointInfo.TimingPoints.Serialize(), Is.EqualTo(expected.beatmap.ControlPointInfo.TimingPoints.Serialize()));
@@ -153,7 +153,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
};
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty))), string.Empty);
var encoded = EncodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty)));
var decodedAfterEncode = DecodeFromLegacy(encoded, beatmaps_resource_store, string.Empty);
var decodedSlider = (Slider)decodedAfterEncode.beatmap.HitObjects[0];
Assert.That(decodedSlider.Path.ControlPoints.Count, Is.EqualTo(4));
Assert.That(decodedSlider.Path.ControlPoints[0].Type, Is.EqualTo(PathType.BSpline(3)));
@@ -182,7 +183,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
};
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty))), string.Empty);
var encoded = EncodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty)));
var decodedAfterEncode = DecodeFromLegacy(encoded, beatmaps_resource_store, string.Empty);
var decodedSlider = (Slider)decodedAfterEncode.beatmap.HitObjects[0];
Assert.That(decodedSlider.Path.ControlPoints.Count, Is.EqualTo(5));
}
@@ -209,7 +211,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
};
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy((new Beatmap(), beatmapSkin)), string.Empty);
var encoded = EncodeToLegacy((new Beatmap(), beatmapSkin));
var decodedAfterEncode = DecodeFromLegacy(encoded, beatmaps_resource_store, string.Empty);
Assert.That(decodedAfterEncode.skin.Configuration.CustomComboColours, Has.Count.EqualTo(8));
}
@@ -231,8 +234,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
HitObjects = { originalSlider }
};
var encoded = encodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty)));
var decodedAfterEncode = decodeFromLegacy(encoded, string.Empty, version: LegacyBeatmapEncoder.FIRST_LAZER_VERSION);
var encoded = EncodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty)));
var decodedAfterEncode = DecodeFromLegacy(encoded, beatmaps_resource_store, string.Empty, version: LegacyBeatmapEncoder.FIRST_LAZER_VERSION);
var decodedSlider = (Slider)decodedAfterEncode.beatmap.HitObjects[0];
Assert.That(decodedSlider.Path.ControlPoints.Select(p => p.Position),
Is.EquivalentTo(originalSlider.Path.ControlPoints.Select(p => p.Position)));
@@ -251,7 +254,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
};
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty))), string.Empty);
var encoded = EncodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty)));
var decodedAfterEncode = DecodeFromLegacy(encoded, beatmaps_resource_store, string.Empty);
Assert.That(decodedAfterEncode.beatmap.HitObjects[0].Samples[0].Suffix, Is.Null);
Assert.That(decodedAfterEncode.beatmap.HitObjects[0].Samples[0].UseBeatmapSamples, Is.False);
@@ -263,7 +267,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(decodedAfterEncode.beatmap.HitObjects[2].Samples[0].UseBeatmapSamples, Is.True);
}
private bool areComboColoursEqual(IHasComboColours a, IHasComboColours b)
private static bool areComboColoursEqual(IHasComboColours a, IHasComboColours b)
{
// equal to null, no need to SequenceEqual
if (a.ComboColours == null && b.ComboColours == null)
@@ -275,7 +279,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
return a.ComboColours.SequenceEqual(b.ComboColours);
}
private void sort(IBeatmap beatmap)
public static void Sort(IBeatmap beatmap)
{
// Sort control points to ensure a sane ordering, as they may be parsed in different orders. This works because each group contains only uniquely-typed control points.
foreach (var g in beatmap.ControlPointInfo.Groups)
@@ -285,19 +289,19 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
private (IBeatmap beatmap, TestLegacySkin skin) decodeFromLegacy(Stream stream, string name, int version = LegacyDecoder<Beatmap>.LATEST_VERSION)
public static (IBeatmap beatmap, TestLegacySkin skin) DecodeFromLegacy(Stream stream, IResourceStore<byte[]> beatmapsResourceStore, string name, int version = LegacyDecoder<Beatmap>.LATEST_VERSION)
{
using (var reader = new LineBufferedReader(stream))
{
var beatmap = new LegacyBeatmapDecoder(version) { ApplyOffsets = false }.Decode(reader);
var beatmapSkin = new TestLegacySkin(beatmaps_resource_store, name);
var beatmapSkin = new TestLegacySkin(beatmapsResourceStore, name);
stream.Seek(0, SeekOrigin.Begin);
beatmapSkin.Configuration = new LegacySkinDecoder().Decode(reader);
return (convert(beatmap), beatmapSkin);
}
}
private class TestLegacySkin : LegacySkin
public class TestLegacySkin : LegacySkin
{
public TestLegacySkin(IResourceStore<byte[]> fallbackStore, string fileName)
: base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, fallbackStore, fileName)
@@ -305,7 +309,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
private MemoryStream encodeToLegacy((IBeatmap beatmap, ISkin skin) fullBeatmap)
public static MemoryStream EncodeToLegacy((IBeatmap beatmap, ISkin skin) fullBeatmap)
{
var (beatmap, beatmapSkin) = fullBeatmap;
var stream = new MemoryStream();
@@ -318,7 +322,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
return stream;
}
private IBeatmap convert(IBeatmap beatmap)
private static IBeatmap convert(IBeatmap beatmap)
{
switch (beatmap.BeatmapInfo.Ruleset.OnlineID)
{
@@ -7,9 +7,11 @@ using NUnit.Framework;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Checks;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Edit.Checks;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
@@ -18,7 +20,7 @@ namespace osu.Game.Tests.Editing.Checks
[TestFixture]
public class CheckFewHitsoundsTest
{
private CheckFewHitsounds check = null!;
private CheckOsuFewHitsounds check = null!;
private List<HitSampleInfo> notHitsounded = null!;
private List<HitSampleInfo> hitsounded = null!;
@@ -26,7 +28,7 @@ namespace osu.Game.Tests.Editing.Checks
[SetUp]
public void Setup()
{
check = new CheckFewHitsounds();
check = new CheckOsuFewHitsounds();
notHitsounded = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) };
hitsounded = new List<HitSampleInfo>
{
@@ -82,6 +84,43 @@ namespace osu.Game.Tests.Editing.Checks
assertOk(hitObjects);
}
[Test]
public void TestRarelyHitsoundedLongWallTimeMostlyBreak()
{
var hitObjects = new List<HitObject>
{
new HitCircle { StartTime = 0, Samples = hitsounded },
new HitCircle { StartTime = 1000, Samples = notHitsounded },
new HitCircle { StartTime = 2000, Samples = notHitsounded },
new HitCircle { StartTime = 3000, Samples = notHitsounded },
new HitCircle { StartTime = 4000, Samples = notHitsounded },
new HitCircle { StartTime = 5000, Samples = notHitsounded },
new HitCircle { StartTime = 10000, Samples = hitsounded },
};
// 10s since last hitsound, but 6s overlap a break → 4s without hitsounds (below warning threshold).
assertOk(hitObjects, new BreakPeriod(4000, 10000));
}
[Test]
public void TestRarelyHitsoundedLongWallTimeMostlySpinner()
{
var hitObjects = new List<HitObject>
{
new HitCircle { StartTime = 0, Samples = hitsounded },
new HitCircle { StartTime = 200, Samples = notHitsounded },
new HitCircle { StartTime = 400, Samples = notHitsounded },
new HitCircle { StartTime = 600, Samples = notHitsounded },
new HitCircle { StartTime = 800, Samples = notHitsounded },
new HitCircle { StartTime = 1000, Samples = notHitsounded },
new Spinner { StartTime = 1200, EndTime = 21200, Samples = notHitsounded },
new HitCircle { StartTime = 21400, Samples = hitsounded },
};
// 21.4s since last hitsound, but 20s overlap a spinner → 1.4s without hitsounds.
assertOk(hitObjects);
}
[Test]
public void TestLightlyHitsounded()
{
@@ -194,9 +233,9 @@ namespace osu.Game.Tests.Editing.Checks
assertOk(hitObjects);
}
private void assertOk(List<HitObject> hitObjects)
private void assertOk(List<HitObject> hitObjects, params BreakPeriod[] breaks)
{
Assert.That(check.Run(getContext(hitObjects)), Is.Empty);
Assert.That(check.Run(getContext(hitObjects, breaks)), Is.Empty);
}
private void assertLongPeriodProblem(List<HitObject> hitObjects, int count = 1)
@@ -231,10 +270,13 @@ namespace osu.Game.Tests.Editing.Checks
Assert.That(issues.Any(issue => issue.Template is CheckFewHitsounds.IssueTemplateNoHitsounds));
}
private BeatmapVerifierContext getContext(List<HitObject> hitObjects)
private BeatmapVerifierContext getContext(List<HitObject> hitObjects, params BreakPeriod[] breaks)
{
var beatmap = new Beatmap<HitObject> { HitObjects = hitObjects };
foreach (var b in breaks)
beatmap.Breaks.Add(b);
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
}
}
@@ -51,7 +51,15 @@ namespace osu.Game.Tests.Extensions
[TestCase(0.4, true, 2, ExpectedResult = "40%")]
[TestCase(1e-6, false, 6, ExpectedResult = "0,000001")]
[TestCase(0.48333, true, 4, ExpectedResult = "48,33%")]
public string TestCultureSensitivity(double input, bool percent, int decimalDigits)
public string TestCultureSensitivityDecimalPoint(double input, bool percent, int decimalDigits)
{
return input.ToStandardFormattedString(decimalDigits, percent);
}
[Test]
[SetCulture("sv-SE")]
[TestCase(-1e-6, false, 6, ExpectedResult = "0,000001")]
public string TestCultureSensitivityNegativeSign(double input, bool percent, int decimalDigits)
{
return input.ToStandardFormattedString(decimalDigits, percent);
}
+61
View File
@@ -0,0 +1,61 @@
// 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.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using osu.Game.IPC;
namespace osu.Game.Tests.IPC
{
public sealed class WebSocketClient : IDisposable
{
public event Action<string>? MessageReceived;
public event Action? Closed;
private readonly int port;
private WebSocketChannel? channel;
public WebSocketClient(int port)
{
this.port = port;
}
public async Task Start(CancellationToken cancellationToken = default)
{
var webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri($@"ws://localhost:{port}/"), cancellationToken);
channel = new WebSocketChannel(webSocket);
channel.MessageReceived += msg => MessageReceived?.Invoke(msg);
channel.ClosedPrematurely += () => Closed?.Invoke();
channel.Start(cancellationToken);
}
public async Task SendAsync(string message)
{
if (channel == null)
throw new InvalidOperationException($@"Must {nameof(Start)} first.");
await channel.SendAsync(message);
}
public async Task StopAsync(CancellationToken stoppingToken = default)
{
try
{
if (channel != null)
await channel.StopAsync(stoppingToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// has to be caught manually because outer task isn't accepting `stoppingToken`.
}
}
public void Dispose()
{
channel?.Dispose();
}
}
}
+274
View File
@@ -0,0 +1,274 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Game.IPC;
using osu.Game.Online.Multiplayer;
namespace osu.Game.Tests.IPC
{
[TestFixture]
public class WebSocketTest
{
[Test]
public async Task TestClientInitiatedDuplexCommunication()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
var duplexComplete = new ManualResetEventSlim(false);
server.MessageReceived += (clientId, msg) =>
{
if (msg != "PING")
return;
// ReSharper disable once AccessToDisposedClosure
server.SendAsync(clientId, "PONG").FireAndForget();
};
client.MessageReceived += msg =>
{
if (msg != "PONG")
return;
duplexComplete.Set();
};
await server.StartAsync();
await client.Start();
await client.SendAsync("PING");
Assert.That(duplexComplete.Wait(10_000));
await client.StopAsync();
await server.StopAsync();
client.Dispose();
server.Dispose();
}
[Test]
public async Task TestServerInitiatedDuplexCommunication()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
var clientConnected = new ManualResetEventSlim();
var duplexComplete = new ManualResetEventSlim();
client.MessageReceived += msg =>
{
if (msg != "PING")
return;
// ReSharper disable once AccessToDisposedClosure
client.SendAsync("PONG").FireAndForget();
};
server.ClientConnected += _ => clientConnected.Set();
server.MessageReceived += (_, msg) =>
{
if (msg != "PONG")
return;
duplexComplete.Set();
};
await server.StartAsync();
await client.Start();
Assert.That(clientConnected.Wait(10_000));
await server.SendAsync(1, "PING");
Assert.That(duplexComplete.Wait(10_000));
await client.StopAsync();
await server.StopAsync();
client.Dispose();
server.Dispose();
}
[Test]
public async Task TestServerBroadcast()
{
const int port = 54321;
const int client_count = 5;
var server = new WebSocketServer(port);
var clients = new List<WebSocketClient>(client_count);
var connectionCountdown = new CountdownEvent(client_count);
var receiptCountdown = new CountdownEvent(client_count);
for (int i = 0; i < client_count; ++i)
{
var client = new WebSocketClient(port);
client.MessageReceived += msg =>
{
if (msg != "HI ALL")
return;
receiptCountdown.Signal();
};
clients.Add(client);
}
server.ClientConnected += _ => connectionCountdown.Signal();
await server.StartAsync();
foreach (var client in clients)
await client.Start();
Assert.That(connectionCountdown.Wait(10_000));
await server.BroadcastAsync("HI ALL");
Assert.That(receiptCountdown.Wait(10_000));
foreach (var client in clients)
{
await client.StopAsync();
client.Dispose();
}
await server.StopAsync();
server.Dispose();
}
[Test]
public async Task TestClientSoftAborts()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
await server.StartAsync();
await client.Start();
await client.StopAsync();
client.Dispose();
await server.StopAsync();
server.Dispose();
}
[Test]
public async Task TestClientHardAborts()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
await server.StartAsync();
await client.Start();
await client.StopAsync(new CancellationToken(true));
client.Dispose();
await server.StopAsync();
server.Dispose();
}
[Test]
public async Task TestServerSoftAborts()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
await server.StartAsync();
await client.Start();
await server.StopAsync();
server.Dispose();
await client.StopAsync();
client.Dispose();
}
[Test]
public async Task TestServerHardAborts()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
await server.StartAsync();
await client.Start();
await server.StopAsync(new CancellationToken(true));
server.Dispose();
await client.StopAsync();
client.Dispose();
}
[Test]
public async Task TestClientMessageTooLong()
{
const int port = 54321;
var server = new WebSocketServer(port);
var client = new WebSocketClient(port);
var clientClosed = new ManualResetEventSlim();
client.Closed += clientClosed.Set;
await server.StartAsync();
await client.Start();
await client.SendAsync(new string('0', 9999));
Assert.That(clientClosed.Wait(10_000));
await client.StopAsync();
client.Dispose();
var client2 = new WebSocketClient(port);
var duplexComplete = new ManualResetEventSlim();
server.MessageReceived += (clientId, msg) =>
{
if (msg != "PING")
return;
// ReSharper disable once AccessToDisposedClosure
server.SendAsync(clientId, "PONG").FireAndForget();
};
client2.MessageReceived += msg =>
{
if (msg != "PONG")
return;
duplexComplete.Set();
};
await client2.Start();
await client2.SendAsync("PING");
Assert.That(duplexComplete.Wait(10000));
await client2.StopAsync();
await server.StopAsync();
client2.Dispose();
server.Dispose();
}
[Test]
public async Task TestStartStopServerWithoutReceivingClients()
{
const int port = 54321;
var server = new WebSocketServer(port);
await server.StartAsync();
await server.StopAsync();
server.Dispose();
}
}
}
+19
View File
@@ -7,8 +7,10 @@ using System.Linq;
using Moq;
using NUnit.Framework;
using NUnit.Framework.Legacy;
using osu.Framework.Bindables;
using osu.Framework.Extensions.TypeExtensions;
using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
@@ -33,6 +35,17 @@ namespace osu.Game.Tests.Mods
Assert.That(invalid, Is.EquivalentTo(new[] { mod.Object }));
}
[Test]
public void TestModIsNotCompatibleWithItselfEvenIfSettingsDiffer()
{
var mod1 = new Mock<CustomMod3>();
var mod2 = new Mock<CustomMod3>();
mod2.Setup(m => m.Setting).Returns(new BindableBool(true));
Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }, out var invalid), Is.False);
Assert.That(invalid, Is.EquivalentTo(new[] { mod2.Object }));
}
[Test]
public void TestModIsCompatibleByItself()
{
@@ -397,6 +410,12 @@ namespace osu.Game.Tests.Mods
{
}
public abstract class CustomMod3 : Mod, IModCompatibilitySpecification
{
[SettingSource("Setting")]
public virtual BindableBool Setting { get; } = new BindableBool();
}
private class InvalidMultiplayerMod : Mod
{
public override string Name => string.Empty;
@@ -147,7 +147,7 @@ namespace osu.Game.Tests.NonVisual
foreach (string file in osuStorage.IgnoreFiles)
{
if (!file.Contains(".realm", StringComparison.Ordinal))
if (!file.Contains(".realm", StringComparison.Ordinal) && !file.Contains("AuthNative", StringComparison.Ordinal))
{
Assert.That(File.Exists(Path.Combine(originalDirectory, file)));
Assert.That(storage.Exists(file), Is.False, () => $"{file} exists in destination when it was expected to be ignored");
@@ -660,7 +660,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
public bool Matches(BeatmapInfo beatmapInfo, FilterCriteria criteria) => match;
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) => false;
public bool FilterMayChangeFromMods(ValueChangedEvent<IReadOnlyList<Mod>> mods) => false;
public bool FilterMayChangeFromMods(FilterCriteria criteria, ValueChangedEvent<IReadOnlyList<Mod>> mods) => false;
}
}
}
@@ -559,7 +559,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
return false;
}
public bool FilterMayChangeFromMods(ValueChangedEvent<IReadOnlyList<Mod>> mods) => false;
public bool FilterMayChangeFromMods(FilterCriteria criteria, ValueChangedEvent<IReadOnlyList<Mod>> mods) => false;
}
private static readonly object[] correct_date_query_examples =
@@ -0,0 +1,8 @@
[
{"beatmapset_id":2529695,"difficulty_rating":7.61701,"id":5590715,"mode":"mania","status":"ranked","total_length":396,"user_id":13371424,"version":"[4K] Memoriae Effervescentes","accuracy":7.2,"ar":5,"bpm":220,"convert":false,"count_circles":2097,"count_sliders":3556,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8.2,"hit_length":395,"is_scoreable":true,"last_updated":"2026-04-21T08:35:33Z","mode_int":3,"passcount":1485,"playcount":4006,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/5590715","checksum":"5b43b30845408f6bf0cc02d19fa475a4","beatmapset":{"anime_cover":false,"artist":"Laur","artist_unicode":"Laur","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/cover.jpg?1776760548","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/cover@2x.jpg?1776760548","card":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/card.jpg?1776760548","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/card@2x.jpg?1776760548","list":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/list.jpg?1776760548","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/list@2x.jpg?1776760548","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/slimcover.jpg?1776760548","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/slimcover@2x.jpg?1776760548"},"creator":"Ainer","favourite_count":55,"genre_id":10,"hype":null,"id":2529695,"language_id":5,"nsfw":false,"offset":0,"play_count":4102,"preview_url":"https:\/\/b.ppy.sh\/preview\/2529695.mp3","source":"osu!mania 7K World Cup 2026","spotlight":false,"status":"ranked","title":"SEV-26","title_unicode":"SEV-26","track_id":11695,"user_id":13371424,"video":false,"bpm":180,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-04-21T08:35:32Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/2191856","nominations_summary":{"current":2,"eligible_main_rulesets":["mania"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-28T09:22:15Z","rating":8.96296,"storyboard":false,"submitted_date":"2026-03-28T00:58:23Z","tags":"featured artist fa mappers' guild mg mpg osu! original electronic instrumental neurofunk hardcore psytrance speedcore tearout dubstep orchestral artcore sev26 sylvatic encephalitis virus grand finals grandfinals gf mwc 2026 world cup mwc2026 mwc7k2026 tb tiebreaker alicia antipole symmatrix- sardines bruh_shen tehfire polytetral hourius naraicat alexdunk lowgraphics sakura006","availability":{"download_disabled":false,"more_information":null},"ratings":[0,3,0,0,0,0,0,0,0,1,23]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,0,0,0,0,0,9,0,9,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,18,18,0,0,0,0,0,0,0,0,9,9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,18,9,9,18,0,18,9,0,0,0,0,0],"exit":[0,36,36,45,63,9,9,54,18,63,72,18,9,0,9,9,0,18,27,18,18,45,0,9,9,9,9,27,9,9,9,9,9,0,0,9,0,9,0,0,0,0,0,0,18,0,0,0,0,9,0,9,0,0,0,0,18,18,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,9,9,0,0,9,18,0,9,0,0,0,0,0,0,0]},"max_combo":9163,"owners":[{"id":13371424,"username":"Ainer"},{"id":17258072,"username":"Alicia"}]},
{"beatmapset_id":2518293,"difficulty_rating":3.61767,"id":5602450,"mode":"mania","status":"ranked","total_length":282,"user_id":8425052,"version":"[4K] Innocence","accuracy":8,"ar":5,"bpm":140,"convert":false,"count_circles":3618,"count_sliders":0,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8,"hit_length":281,"is_scoreable":true,"last_updated":"2026-04-13T00:00:32Z","mode_int":3,"passcount":369,"playcount":1963,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/5602450","checksum":"51319d68b9373eb98807ea5a2187d803","beatmapset":{"anime_cover":false,"artist":"rejection","artist_unicode":"rejection","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/cover.jpg?1776038446","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/cover@2x.jpg?1776038446","card":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/card.jpg?1776038446","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/card@2x.jpg?1776038446","list":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/list.jpg?1776038446","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/list@2x.jpg?1776038446","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/slimcover.jpg?1776038446","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/slimcover@2x.jpg?1776038446"},"creator":"Stella-","favourite_count":48,"genre_id":10,"hype":null,"id":2518293,"language_id":3,"nsfw":false,"offset":0,"play_count":2031,"preview_url":"https:\/\/b.ppy.sh\/preview\/2518293.mp3","source":"","spotlight":false,"status":"ranked","title":"White Canvas (feat. Aitsuki Nakuru)","title_unicode":"White Canvas (feat. \u85cd\u6708\u306a\u304f\u308b)","track_id":5057,"user_id":8425052,"video":false,"bpm":140,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-04-13T00:00:32Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/2185354","nominations_summary":{"current":2,"eligible_main_rulesets":["mania"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-21T23:42:39Z","rating":8.66667,"storyboard":false,"submitted_date":"2026-03-06T10:45:47Z","tags":"fa featured artist japanese pop jpop j-pop electronic encore emotional -emotional vocal pop 02- 2 megarex mrx-074 mrx074 female vocals vocalist m3-2020\u79cb fall m3-46 muse dash the future","availability":{"download_disabled":false,"more_information":null},"ratings":[0,0,0,0,0,1,0,1,0,0,4]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,0,9,0,9,0,0,9,0,0,0,0,0,0,0,0,0,0,0,9,0,9,0,0,0,9,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"exit":[0,0,18,18,9,63,9,27,18,9,18,9,18,9,27,9,36,0,0,9,9,0,9,0,18,27,0,0,0,9,9,0,0,0,0,9,0,9,0,9,9,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,9,0,0,0,9,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"max_combo":3618,"owners":[{"id":8425052,"username":"Stella-"}]},
{"beatmapset_id":1665948,"difficulty_rating":4.87529,"id":3871759,"mode":"mania","status":"ranked","total_length":379,"user_id":17272017,"version":"[4K] Time Freeze Illusion","accuracy":8.5,"ar":5,"bpm":200,"convert":false,"count_circles":5264,"count_sliders":629,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8,"hit_length":377,"is_scoreable":true,"last_updated":"2026-04-13T09:52:53Z","mode_int":3,"passcount":308,"playcount":1738,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/3871759","checksum":"34b98351e32ca3db24ea9fc0055a923b","beatmapset":{"anime_cover":true,"artist":"Release Hallucination","artist_unicode":"Release Hallucination","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/cover.jpg?1776073987","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/cover@2x.jpg?1776073987","card":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/card.jpg?1776073987","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/card@2x.jpg?1776073987","list":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/list.jpg?1776073987","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/list@2x.jpg?1776073987","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/slimcover.jpg?1776073987","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/slimcover@2x.jpg?1776073987"},"creator":"Lazurent","favourite_count":51,"genre_id":11,"hype":null,"id":1665948,"language_id":3,"nsfw":false,"offset":0,"play_count":4811,"preview_url":"https:\/\/b.ppy.sh\/preview\/1665948.mp3","source":"","spotlight":false,"status":"ranked","title":"Chronostasis","title_unicode":"Chronostasis","track_id":4954,"user_id":17272017,"video":false,"bpm":200,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-04-13T09:52:53Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/1494910","nominations_summary":{"current":2,"eligible_main_rulesets":["mania"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-20T11:25:45Z","rating":8.33333,"storyboard":false,"submitted_date":"2022-01-03T14:00:15Z","tags":"m3-38 symphonic progressive metal marathon emi gothic kaorin kaoru hirato japanese \u30af\u30ed\u30ce\u30b9\u30bf\u30b7\u30b9 fa featured artist mg mpg mappers' guild l43yrnt","availability":{"download_disabled":false,"more_information":null},"ratings":[0,1,0,0,0,0,0,0,0,1,4]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,0,9,9,63,18,45,18,45,27,0,0,0,0,0,9,0,0,0,0,9,18,0,0,0,9,9,0,0,0,0,0,18,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0],"exit":[0,0,18,63,63,135,261,216,126,81,81,36,18,27,45,27,27,36,9,9,0,18,18,81,27,18,9,18,18,9,0,0,0,36,45,36,0,0,0,0,0,0,9,9,0,18,0,0,9,0,9,0,9,0,0,9,0,9,9,0,0,9,0,0,0,9,0,0,0,0,0,0,0,0,0,9,9,0,0,0,0,0,9,0,0,0,0,0,0,0,0,9,0,0,9,0,0,0,0,9]},"max_combo":6974,"owners":[{"id":17272017,"username":"Lazurent"}]},
{"beatmapset_id":2535368,"difficulty_rating":5.82018,"id":5606802,"mode":"mania","status":"ranked","total_length":296,"user_id":10085090,"version":"[4K] Desperate Soul","accuracy":8.5,"ar":5,"bpm":240,"convert":false,"count_circles":6288,"count_sliders":0,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8,"hit_length":290,"is_scoreable":true,"last_updated":"2026-04-16T18:03:43Z","mode_int":3,"passcount":208,"playcount":1738,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/5606802","checksum":"55451c2f43654e4ec7b9bb156b93148e","beatmapset":{"anime_cover":false,"artist":"Imperial Circus Dead Decadence","artist_unicode":"Imperial Circus Dead Decadence","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/cover.jpg?1776362637","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/cover@2x.jpg?1776362637","card":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/card.jpg?1776362637","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/card@2x.jpg?1776362637","list":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/list.jpg?1776362637","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/list@2x.jpg?1776362637","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/slimcover.jpg?1776362637","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/slimcover@2x.jpg?1776362637"},"creator":"Carpihat","favourite_count":48,"genre_id":11,"hype":null,"id":2535368,"language_id":3,"nsfw":false,"offset":0,"play_count":1961,"preview_url":"https:\/\/b.ppy.sh\/preview\/2535368.mp3","source":"","spotlight":false,"status":"ranked","title":"Jashin no Konrei, Gi wa Ai to Shiru.","title_unicode":"\u90aa\u795e\u306e\u5a5a\u793c\u3001\u5100\u306f\u611b\u3068\u77e5\u308b\u3002","track_id":862,"user_id":10085090,"video":false,"bpm":240,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-04-16T18:03:42Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/2195210","nominations_summary":{"current":2,"eligible_main_rulesets":["mania"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-18T18:05:56Z","rating":10,"storyboard":false,"submitted_date":"2026-04-07T09:36:37Z","tags":"cthulhu wedding blackened melodic symphonic death black metal melodeath icdd \u72c2\u304a\u3057\u304f\u54b2\u3044\u305f\u51c4\u60e8\u306a\u9ab8\u306f\u594f\u3067\u3001\u611b\u304a\u3057\u304f\u88c2\u3044\u305f\u5c11\u5973\u306f\u8056\u9910\u306e\u8a5e\u3092\u8b33\u3046\u3002 kurooshiku saita seisan na mukuro wa kanaderu itooshiku shoujo seisen no kotoba wo utau fa featured artist japanese mpg mg mappers' guild","availability":{"download_disabled":false,"more_information":null},"ratings":[0,0,0,0,0,0,0,0,0,0,1]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,9,27,27,27,9,9,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"exit":[0,0,0,27,63,117,108,36,63,36,9,45,9,27,9,0,9,9,0,36,9,36,0,9,0,9,9,0,0,0,27,9,9,0,0,0,0,0,9,0,9,27,9,0,9,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"max_combo":6288,"owners":[{"id":10085090,"username":"Carpihat"}]},
{"beatmapset_id":2348607,"difficulty_rating":5.57212,"id":5140134,"mode":"mania","status":"ranked","total_length":263,"user_id":23384715,"version":"[4K] Enraged Apparition","accuracy":8,"ar":10,"bpm":250,"convert":false,"count_circles":4682,"count_sliders":120,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8,"hit_length":251,"is_scoreable":true,"last_updated":"2026-03-26T20:54:51Z","mode_int":3,"passcount":404,"playcount":2674,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/5140134","checksum":"a2dd0c1b8aa0d3396b47621b758c6adf","beatmapset":{"anime_cover":true,"artist":"Imperial Circus Dead Decadence","artist_unicode":"Imperial Circus Dead Decadence","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/cover.jpg?1774558514","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/cover@2x.jpg?1774558514","card":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/card.jpg?1774558514","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/card@2x.jpg?1774558514","list":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/list.jpg?1774558514","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/list@2x.jpg?1774558514","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/slimcover.jpg?1774558514","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/slimcover@2x.jpg?1774558514"},"creator":"Sayuka","favourite_count":435,"genre_id":11,"hype":null,"id":2348607,"language_id":3,"nsfw":false,"offset":0,"play_count":118018,"preview_url":"https:\/\/b.ppy.sh\/preview\/2348607.mp3","source":"","spotlight":false,"status":"ranked","title":"Shinbatsu o Tadori Kyoukotsu ni Itaru","title_unicode":"\u795e\u7f70\u3092\u8fbf\u308a\u72c2\u9aa8\u306b\u81f3\u308b","track_id":8223,"user_id":11322604,"video":false,"bpm":250,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-03-26T20:54:46Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/2061550","nominations_summary":{"current":3,"eligible_main_rulesets":["osu"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-12T17:43:03Z","rating":9.11539,"storyboard":false,"submitted_date":"2025-04-02T11:30:44Z","tags":"maaadbot rhythmnoodles ciiyus icdd sinyus20 oomf chan ciyus miapah fort danilmaz1 mekadon -aly arthro roupus julie maiev worthlessnut9 frawog mithew m1ts shoyeu take amats sprixx chocomilk \u9ec4\u6cc9\u3088\u308a\u8074\u3053\u3086\u3001\u7687\u56fd\u306e\u71c8\u3068\u7114\u306e\u5c11\u5973\u3002 japanese male vocals symphonic melodic black death metal doujin hull kim rib:y(uhki) rib yuhki shuhei js jumpstream hs handstream stream stamina jack chordjack rice fa featured artist","availability":{"download_disabled":false,"more_information":null},"ratings":[0,7,0,0,1,0,1,1,3,10,81]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,18,72,54,36,36,18,9,9,9,0,0,0,9,63,18,0,0,0,0,0,0,9,0,0,0,9,9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0],"exit":[0,0,9,243,90,198,216,126,18,81,63,36,9,0,18,135,45,27,27,18,18,9,0,0,0,0,9,9,18,27,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,27,18,9,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,9,0,27,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,9,9,0]},"max_combo":5113,"owners":[{"id":16018038,"username":"frawog"},{"id":23384715,"username":"Worthlessnut9"}]}
]
@@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
using osu.Framework.Testing;
using osu.Game.Audio;
using osu.Game.Configuration;
using osu.Game.Rulesets.Osu;
using osu.Game.Skinning;
using osu.Game.Tests.Beatmaps;
@@ -26,17 +25,15 @@ namespace osu.Game.Tests.Skins
public partial class TestSceneBeatmapSkinLookupDisables : OsuTestScene
{
private UserSkinSource userSource;
private BeatmapSkinProvidingContainer beatmapSkinProvider;
private BeatmapSkinSource beatmapSource;
private SkinRequester requester;
[Resolved]
private OsuConfigManager config { get; set; }
[SetUp]
public void SetUp() => Schedule(() =>
{
Add(new SkinProvidingContainer(userSource = new UserSkinSource())
.WithChild(new BeatmapSkinProvidingContainer(beatmapSource = new BeatmapSkinSource())
.WithChild(beatmapSkinProvider = new BeatmapSkinProvidingContainer(beatmapSource = new BeatmapSkinSource())
.WithChild(requester = new SkinRequester())));
});
@@ -44,7 +41,7 @@ namespace osu.Game.Tests.Skins
[TestCase(true)]
public void TestDrawableLookup(bool allowBeatmapLookups)
{
AddStep($"Set beatmap skin enabled to {allowBeatmapLookups}", () => config.SetValue(OsuSetting.BeatmapSkins, allowBeatmapLookups));
AddStep($"Set beatmap skin enabled to {allowBeatmapLookups}", () => beatmapSkinProvider.BeatmapSkins.Value = allowBeatmapLookups);
string expected = allowBeatmapLookups ? "beatmap" : "user";
@@ -55,7 +52,7 @@ namespace osu.Game.Tests.Skins
[TestCase(true)]
public void TestProviderLookup(bool allowBeatmapLookups)
{
AddStep($"Set beatmap skin enabled to {allowBeatmapLookups}", () => config.SetValue(OsuSetting.BeatmapSkins, allowBeatmapLookups));
AddStep($"Set beatmap skin enabled to {allowBeatmapLookups}", () => beatmapSkinProvider.BeatmapSkins.Value = allowBeatmapLookups);
ISkin expected() => allowBeatmapLookups ? beatmapSource : userSource;
@@ -100,8 +100,10 @@ namespace osu.Game.Tests.Visual.Editing
[Test]
public void TestPlacementOfConcurrentObjectWithDuration()
{
AddStep("seek to timing point", () => EditorClock.Seek(2170));
AddStep("add hit circle", () => EditorBeatmap.Add(createHitCircle(2170, Vector2.Zero)));
const double spinner_start_time = 2170;
const double spinner_end_seek_time = 2500;
AddStep("seek to timing point", () => EditorClock.Seek(spinner_start_time));
AddStep("choose spinner placement tool", () =>
{
@@ -116,10 +118,15 @@ namespace osu.Game.Tests.Visual.Editing
});
AddStep("end placing spinner", () =>
{
EditorClock.Seek(2500);
EditorClock.Seek(spinner_end_seek_time);
InputManager.Click(MouseButton.Right);
});
AddStep("add hit circle mid-spinner", () =>
{
EditorBeatmap.Add(createHitCircle((spinner_start_time + spinner_end_seek_time) / 2, Vector2.Zero));
});
AddAssert("two timeline blueprints present", () => Editor.ChildrenOfType<TimelineHitObjectBlueprint>().Count() == 2);
}

Some files were not shown because too many files have changed in this diff Show More