diff --git a/.github/ISSUE_TEMPLATE/01-bug-issues.md b/.github/ISSUE_TEMPLATE/01-bug-issues.md
index e45893b97a..7026179259 100644
--- a/.github/ISSUE_TEMPLATE/01-bug-issues.md
+++ b/.github/ISSUE_TEMPLATE/01-bug-issues.md
@@ -25,6 +25,6 @@ Please check:
*please attach logs here, which are located at:*
- `%AppData%/osu/logs` *(on Windows),*
- `~/.local/share/osu/logs` *(on Linux & macOS).*
-- `Android/Data/sh.ppy.osulazer/logs` *(on Android)*,
+- `Android/data/sh.ppy.osulazer/files/logs` *(on Android)*,
- on iOS they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer)
-->
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..9e9af23b27
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,46 @@
+version: 2
+updates:
+- package-ecosystem: nuget
+ directory: "/"
+ schedule:
+ interval: monthly
+ time: "17:00"
+ open-pull-requests-limit: 99
+ ignore:
+ - dependency-name: Microsoft.EntityFrameworkCore.Design
+ versions:
+ - "> 2.2.6"
+ - dependency-name: Microsoft.EntityFrameworkCore.Sqlite
+ versions:
+ - "> 2.2.6"
+ - dependency-name: Microsoft.EntityFrameworkCore.Sqlite.Core
+ versions:
+ - "> 2.2.6"
+ - dependency-name: Microsoft.Extensions.DependencyInjection
+ versions:
+ - ">= 5.a, < 6"
+ - dependency-name: NUnit3TestAdapter
+ versions:
+ - ">= 3.16.a, < 3.17"
+ - dependency-name: Microsoft.NET.Test.Sdk
+ versions:
+ - 16.9.1
+ - dependency-name: Microsoft.Extensions.DependencyInjection
+ versions:
+ - 3.1.11
+ - 3.1.12
+ - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson
+ versions:
+ - 3.1.11
+ - dependency-name: Microsoft.NETCore.Targets
+ versions:
+ - 5.0.0
+ - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack
+ versions:
+ - 5.0.2
+ - dependency-name: NUnit
+ versions:
+ - 3.13.1
+ - dependency-name: Microsoft.AspNetCore.SignalR.Client
+ versions:
+ - 3.1.11
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6c327f01b3..e14be20642 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -24,7 +24,7 @@ Issues, bug reports and feature suggestions are welcomed, though please keep in
* the in-game logs, which are located at:
* `%AppData%/osu/logs` (on Windows),
* `~/.local/share/osu/logs` (on Linux and macOS),
- * `Android/Data/sh.ppy.osulazer/logs` (on Android),
+ * `Android/data/sh.ppy.osulazer/files/logs` (on Android),
* on iOS they can be obtained by connecting your device to your desktop and [copying the `logs` directory from the app's own document storage using iTunes](https://support.apple.com/en-us/HT201301#copy-to-computer),
* your system specifications (including the operating system and platform you are playing on),
* a reproduction scenario (list of steps you have performed leading up to the occurrence of the bug),
diff --git a/README.md b/README.md
index 0d6af2aeba..eb790ca18f 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commo
This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
-**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passses come at the end of development, preceeded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to the stable releases of osu! (found on the website). We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
+**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passes come at the end of development, preceded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to the stable releases of osu! (found on the website). We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
index 98a32f9b3a..992f954a3a 100644
--- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
+++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index afa7b03536..7571d1827a 100644
--- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
index c9f87a8551..1c8ed54440 100644
--- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index afa7b03536..7571d1827a 100644
--- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/osu.Android.props b/osu.Android.props
index ed2b27e1c7..80b1c5b52f 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,7 +51,7 @@
-
-
+
+
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 0c21c75290..4a28ab3722 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -9,6 +9,7 @@ using System.Reflection;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using Microsoft.Win32;
+using osu.Desktop.Security;
using osu.Desktop.Overlays;
using osu.Framework.Platform;
using osu.Game;
@@ -113,6 +114,8 @@ namespace osu.Desktop
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
LoadComponentAsync(new GameplayWinKeyBlocker(), Add);
+
+ LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
}
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs
new file mode 100644
index 0000000000..01458b4c37
--- /dev/null
+++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs
@@ -0,0 +1,83 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Security.Principal;
+using osu.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
+
+namespace osu.Desktop.Security
+{
+ ///
+ /// Checks if the game is running with elevated privileges (as admin in Windows, root in Unix) and displays a warning notification if so.
+ ///
+ public class ElevatedPrivilegesChecker : Component
+ {
+ [Resolved]
+ private NotificationOverlay notifications { get; set; }
+
+ private bool elevated;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ elevated = checkElevated();
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ if (elevated)
+ notifications.Post(new ElevatedPrivilegesNotification());
+ }
+
+ private bool checkElevated()
+ {
+ try
+ {
+ switch (RuntimeInfo.OS)
+ {
+ case RuntimeInfo.Platform.Windows:
+ if (!OperatingSystem.IsWindows()) return false;
+
+ var windowsIdentity = WindowsIdentity.GetCurrent();
+ var windowsPrincipal = new WindowsPrincipal(windowsIdentity);
+
+ return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
+
+ case RuntimeInfo.Platform.macOS:
+ case RuntimeInfo.Platform.Linux:
+ return Mono.Unix.Native.Syscall.geteuid() == 0;
+ }
+ }
+ catch
+ {
+ }
+
+ return false;
+ }
+
+ private class ElevatedPrivilegesNotification : SimpleNotification
+ {
+ public override bool IsImportant => true;
+
+ public ElevatedPrivilegesNotification()
+ {
+ Text = $"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance, may break integrations and poses a security risk. Please run the game as a normal user.";
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, NotificationOverlay notificationOverlay)
+ {
+ Icon = FontAwesome.Solid.ShieldAlt;
+ IconBackgound.Colour = colours.YellowDark;
+ }
+ }
+ }
+}
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 3e0f0cb7f6..ad5c323e9b 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -25,6 +25,7 @@
+
diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
index ea43d9a54c..bfcf4ef35e 100644
--- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
+++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
index 48efd73222..517027a9fc 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.UI;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
+using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration;
@@ -20,6 +21,7 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
+using osuTK;
namespace osu.Game.Rulesets.Catch.Tests
{
@@ -170,16 +172,25 @@ namespace osu.Game.Rulesets.Catch.Tests
}
[Test]
- public void TestCatcherStacking()
+ public void TestCatcherRandomStacking()
+ {
+ AddStep("catch more fruits", () => attemptCatch(() => new Fruit
+ {
+ X = (RNG.NextSingle() - 0.5f) * Catcher.CalculateCatchWidth(Vector2.One)
+ }, 50));
+ }
+
+ [Test]
+ public void TestCatcherStackingSameCaughtPosition()
{
AddStep("catch fruit", () => attemptCatch(new Fruit()));
checkPlate(1);
- AddStep("catch more fruits", () => attemptCatch(new Fruit(), 9));
+ AddStep("catch more fruits", () => attemptCatch(() => new Fruit(), 9));
checkPlate(10);
AddAssert("caught objects are stacked", () =>
- catcher.CaughtObjects.All(obj => obj.Y <= 0) &&
- catcher.CaughtObjects.Any(obj => obj.Y == 0) &&
- catcher.CaughtObjects.Any(obj => obj.Y < -20));
+ catcher.CaughtObjects.All(obj => obj.Y <= Catcher.CAUGHT_FRUIT_VERTICAL_OFFSET) &&
+ catcher.CaughtObjects.Any(obj => obj.Y == Catcher.CAUGHT_FRUIT_VERTICAL_OFFSET) &&
+ catcher.CaughtObjects.Any(obj => obj.Y < -25));
}
[Test]
@@ -189,11 +200,11 @@ namespace osu.Game.Rulesets.Catch.Tests
AddStep("catch tiny droplet", () => attemptCatch(new TinyDroplet()));
AddAssert("tiny droplet is exploded", () => catcher.CaughtObjects.Count() == 1 && droppedObjectContainer.Count == 1);
AddUntilStep("wait explosion", () => !droppedObjectContainer.Any());
- AddStep("catch more fruits", () => attemptCatch(new Fruit(), 9));
+ AddStep("catch more fruits", () => attemptCatch(() => new Fruit(), 9));
AddStep("explode", () => catcher.Explode());
AddAssert("fruits are exploded", () => !catcher.CaughtObjects.Any() && droppedObjectContainer.Count == 10);
AddUntilStep("wait explosion", () => !droppedObjectContainer.Any());
- AddStep("catch fruits", () => attemptCatch(new Fruit(), 10));
+ AddStep("catch fruits", () => attemptCatch(() => new Fruit(), 10));
AddStep("drop", () => catcher.Drop());
AddAssert("fruits are dropped", () => !catcher.CaughtObjects.Any() && droppedObjectContainer.Count == 10);
}
@@ -222,10 +233,15 @@ namespace osu.Game.Rulesets.Catch.Tests
private void checkHyperDash(bool state) => AddAssert($"catcher is {(state ? "" : "not ")}hyper dashing", () => catcher.HyperDashing == state);
- private void attemptCatch(CatchHitObject hitObject, int count = 1)
+ private void attemptCatch(CatchHitObject hitObject)
+ {
+ attemptCatch(() => hitObject, 1);
+ }
+
+ private void attemptCatch(Func hitObject, int count)
{
for (var i = 0; i < count; i++)
- attemptCatch(hitObject, out _, out _);
+ attemptCatch(hitObject(), out _, out _);
}
private void attemptCatch(CatchHitObject hitObject, out DrawableCatchHitObject drawableObject, out JudgementResult result)
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
index 1cbfa6338e..ad404e1f63 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
@@ -8,6 +8,8 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
+using osu.Framework.Threading;
+using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration;
@@ -31,12 +33,32 @@ namespace osu.Game.Rulesets.Catch.Tests
private float circleSize;
+ private ScheduledDelegate addManyFruit;
+
+ private BeatmapDifficulty beatmapDifficulty;
+
public TestSceneCatcherArea()
{
AddSliderStep("circle size", 0, 8, 5, createCatcher);
AddToggleStep("hyper dash", t => this.ChildrenOfType().ForEach(area => area.ToggleHyperDash(t)));
- AddStep("catch fruit", () => attemptCatch(new Fruit()));
+ AddStep("catch centered fruit", () => attemptCatch(new Fruit()));
+ AddStep("catch many random fruit", () =>
+ {
+ int count = 50;
+
+ addManyFruit?.Cancel();
+ addManyFruit = Scheduler.AddDelayed(() =>
+ {
+ attemptCatch(new Fruit
+ {
+ X = (RNG.NextSingle() - 0.5f) * Catcher.CalculateCatchWidth(beatmapDifficulty) * 0.6f,
+ });
+
+ if (count-- == 0)
+ addManyFruit?.Cancel();
+ }, 50, true);
+ });
AddStep("catch fruit last in combo", () => attemptCatch(new Fruit { LastInCombo = true }));
AddStep("catch kiai fruit", () => attemptCatch(new TestSceneCatcher.TestKiaiFruit()));
AddStep("miss last in combo", () => attemptCatch(new Fruit { X = 100, LastInCombo = true }));
@@ -45,10 +67,7 @@ namespace osu.Game.Rulesets.Catch.Tests
private void attemptCatch(Fruit fruit)
{
fruit.X = fruit.OriginalX + catcher.X;
- fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty
- {
- CircleSize = circleSize
- });
+ fruit.ApplyDefaults(new ControlPointInfo(), beatmapDifficulty);
foreach (var area in this.ChildrenOfType())
{
@@ -71,6 +90,11 @@ namespace osu.Game.Rulesets.Catch.Tests
{
circleSize = size;
+ beatmapDifficulty = new BeatmapDifficulty
+ {
+ CircleSize = circleSize
+ };
+
SetContents(() =>
{
var droppedObjectContainer = new Container
@@ -84,7 +108,7 @@ namespace osu.Game.Rulesets.Catch.Tests
Children = new Drawable[]
{
droppedObjectContainer,
- new TestCatcherArea(droppedObjectContainer, new BeatmapDifficulty { CircleSize = size })
+ new TestCatcherArea(droppedObjectContainer, beatmapDifficulty)
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
index c2d9a923d9..77e9d672e3 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -3,7 +3,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index f4ddbd3021..e3c457693e 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -114,6 +114,7 @@ namespace osu.Game.Rulesets.Catch
return new Mod[]
{
new CatchModDifficultyAdjust(),
+ new CatchModClassic(),
};
case ModType.Automation:
@@ -126,7 +127,8 @@ namespace osu.Game.Rulesets.Catch
case ModType.Fun:
return new Mod[]
{
- new MultiMod(new ModWindUp(), new ModWindDown())
+ new MultiMod(new ModWindUp(), new ModWindDown()),
+ new CatchModFloatingFruits()
};
default:
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModClassic.cs b/osu.Game.Rulesets.Catch/Mods/CatchModClassic.cs
new file mode 100644
index 0000000000..9624e84018
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModClassic.cs
@@ -0,0 +1,11 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Rulesets.Mods;
+
+namespace osu.Game.Rulesets.Catch.Mods
+{
+ public class CatchModClassic : ModClassic
+ {
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFloatingFruits.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFloatingFruits.cs
new file mode 100644
index 0000000000..63203dd57c
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModFloatingFruits.cs
@@ -0,0 +1,29 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Mods
+{
+ public class CatchModFloatingFruits : Mod, IApplicableToDrawableRuleset
+ {
+ public override string Name => "Floating Fruits";
+ public override string Acronym => "FF";
+ public override string Description => "The fruits are... floating?";
+ public override double ScoreMultiplier => 1;
+ public override IconUsage? Icon => FontAwesome.Solid.Cloud;
+
+ public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
+ {
+ drawableRuleset.Anchor = Anchor.Centre;
+ drawableRuleset.Origin = Anchor.Centre;
+
+ drawableRuleset.Scale = new Vector2(1, -1);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs
index 5d57e84b75..0d6a577d1e 100644
--- a/osu.Game.Rulesets.Catch/UI/Catcher.cs
+++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs
@@ -53,6 +53,16 @@ namespace osu.Game.Rulesets.Catch.UI
///
public const double BASE_SPEED = 1.0;
+ ///
+ /// The amount by which caught fruit should be offset from the plate surface to make them look visually "caught".
+ ///
+ public const float CAUGHT_FRUIT_VERTICAL_OFFSET = -5;
+
+ ///
+ /// The amount by which caught fruit should be scaled down to fit on the plate.
+ ///
+ private const float caught_fruit_scale_adjust = 0.5f;
+
[NotNull]
private readonly Container trailsTarget;
@@ -202,13 +212,13 @@ namespace osu.Game.Rulesets.Catch.UI
/// Calculates the width of the area used for attempting catches in gameplay.
///
/// The scale of the catcher.
- internal static float CalculateCatchWidth(Vector2 scale) => CatcherArea.CATCHER_SIZE * Math.Abs(scale.X) * ALLOWED_CATCH_RANGE;
+ public static float CalculateCatchWidth(Vector2 scale) => CatcherArea.CATCHER_SIZE * Math.Abs(scale.X) * ALLOWED_CATCH_RANGE;
///
/// Calculates the width of the area used for attempting catches in gameplay.
///
/// The beatmap difficulty.
- internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(calculateScale(difficulty));
+ public static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(calculateScale(difficulty));
///
/// Determine if this catcher can catch a in the current position.
@@ -240,7 +250,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (result.IsHit)
{
- var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplaySize.X / 2);
+ var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplaySize.X);
if (CatchFruitOnPlate)
placeCaughtObject(palpableObject, positionInStack);
@@ -384,16 +394,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
updateTrailVisibility();
- if (hyperDashing)
- {
- this.FadeColour(hyperDashColour, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
- this.FadeTo(0.2f, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
- }
- else
- {
- this.FadeColour(Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
- this.FadeTo(1f, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
- }
+ this.FadeColour(hyperDashing ? hyperDashColour : Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
}
private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing;
@@ -479,7 +480,7 @@ namespace osu.Game.Rulesets.Catch.UI
caughtObject.CopyStateFrom(drawableObject);
caughtObject.Anchor = Anchor.TopCentre;
caughtObject.Position = position;
- caughtObject.Scale /= 2;
+ caughtObject.Scale *= caught_fruit_scale_adjust;
caughtObjectContainer.Add(caughtObject);
@@ -489,19 +490,21 @@ namespace osu.Game.Rulesets.Catch.UI
private Vector2 computePositionInStack(Vector2 position, float displayRadius)
{
- const float radius_div_2 = CatchHitObject.OBJECT_RADIUS / 2;
- const float allowance = 10;
+ // this is taken from osu-stable (lenience should be 10 * 10 at standard scale).
+ const float lenience_adjust = 10 / CatchHitObject.OBJECT_RADIUS;
- while (caughtObjectContainer.Any(f => Vector2Extensions.Distance(f.Position, position) < (displayRadius + radius_div_2) / (allowance / 2)))
+ float adjustedRadius = displayRadius * lenience_adjust;
+ float checkDistance = MathF.Pow(adjustedRadius, 2);
+
+ // offset fruit vertically to better place "above" the plate.
+ position.Y += CAUGHT_FRUIT_VERTICAL_OFFSET;
+
+ while (caughtObjectContainer.Any(f => Vector2Extensions.DistanceSquared(f.Position, position) < checkDistance))
{
- float diff = (displayRadius + radius_div_2) / allowance;
-
- position.X += (RNG.NextSingle() - 0.5f) * diff * 2;
- position.Y -= RNG.NextSingle() * diff;
+ position.X += RNG.NextSingle(-adjustedRadius, adjustedRadius);
+ position.Y -= RNG.NextSingle(0, 5);
}
- position.X = Math.Clamp(position.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2);
-
return position;
}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs
index 42ea12214f..668487f673 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs
@@ -324,6 +324,33 @@ namespace osu.Game.Rulesets.Mania.Tests
assertTailJudgement(HitResult.Ok);
}
+ [Test]
+ public void TestZeroLength()
+ {
+ var beatmap = new Beatmap
+ {
+ HitObjects =
+ {
+ new HoldNote
+ {
+ StartTime = 1000,
+ Duration = 0,
+ Column = 0,
+ },
+ },
+ BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
+ };
+
+ performTest(new List
+ {
+ new ManiaReplayFrame(beatmap.HitObjects[0].StartTime, ManiaAction.Key1),
+ new ManiaReplayFrame(beatmap.HitObjects[0].GetEndTime() + 1),
+ }, beatmap);
+
+ AddAssert("hold note hit", () => judgementResults.Where(j => beatmap.HitObjects[0].NestedHitObjects.Contains(j.HitObject))
+ .All(j => j.Type.IsHit()));
+ }
+
private void assertHeadJudgement(HitResult result)
=> AddAssert($"head judged as {result}", () => judgementResults.First(j => j.HitObject is Note).Type == result);
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs
new file mode 100644
index 0000000000..e14ad92842
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs
@@ -0,0 +1,80 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Tests.Visual;
+using osu.Framework.Timing;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.Configuration;
+using osu.Framework.Bindables;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ [TestFixture]
+ public class TestSceneTimingBasedNoteColouring : OsuTestScene
+ {
+ [Resolved]
+ private RulesetConfigCache configCache { get; set; }
+
+ private readonly Bindable configTimingBasedNoteColouring = new Bindable();
+
+ protected override void LoadComplete()
+ {
+ const double beat_length = 500;
+
+ var ruleset = new ManiaRuleset();
+
+ var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 })
+ {
+ HitObjects =
+ {
+ new Note { StartTime = 0 },
+ new Note { StartTime = beat_length / 16 },
+ new Note { StartTime = beat_length / 12 },
+ new Note { StartTime = beat_length / 8 },
+ new Note { StartTime = beat_length / 6 },
+ new Note { StartTime = beat_length / 4 },
+ new Note { StartTime = beat_length / 3 },
+ new Note { StartTime = beat_length / 2 },
+ new Note { StartTime = beat_length }
+ },
+ ControlPointInfo = new ControlPointInfo(),
+ BeatmapInfo = { Ruleset = ruleset.RulesetInfo },
+ };
+
+ foreach (var note in beatmap.HitObjects)
+ {
+ note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+ }
+
+ beatmap.ControlPointInfo.Add(0, new TimingControlPoint
+ {
+ BeatLength = beat_length
+ });
+
+ Child = new Container
+ {
+ Clock = new FramedClock(new ManualClock()),
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Children = new[]
+ {
+ ruleset.CreateDrawableRulesetWith(beatmap)
+ }
+ };
+
+ var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
+ config.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring);
+
+ AddStep("Enable", () => configTimingBasedNoteColouring.Value = true);
+ AddStep("Disable", () => configTimingBasedNoteColouring.Value = false);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
index 64e934efd2..8f8b99b092 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -3,7 +3,7 @@
-
+
diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
index 39d0f4bae4..ac8168dfc9 100644
--- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
+++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
@@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
SetDefault(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5);
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
+ SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false);
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
@@ -34,6 +35,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
public enum ManiaRulesetSetting
{
ScrollTime,
- ScrollDirection
+ ScrollDirection,
+ TimingBasedNoteColouring
}
}
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs
index a13afdfffe..093a8da24f 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs
@@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return;
base.OnMouseUp(e);
- EndPlacement(true);
+ EndPlacement(HitObject.Duration > 0);
}
private double originalStartTime;
diff --git a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditorRuleset.cs
similarity index 78%
rename from osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs
rename to osu.Game.Rulesets.Mania/Edit/DrawableManiaEditorRuleset.cs
index 445df79f6f..b0af8c503b 100644
--- a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs
+++ b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditorRuleset.cs
@@ -12,16 +12,16 @@ using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.Edit
{
- public class DrawableManiaEditRuleset : DrawableManiaRuleset
+ public class DrawableManiaEditorRuleset : DrawableManiaRuleset
{
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
- public DrawableManiaEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods)
+ public DrawableManiaEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods)
: base(ruleset, beatmap, mods)
{
}
- protected override Playfield CreatePlayfield() => new ManiaEditPlayfield(Beatmap.Stages)
+ protected override Playfield CreatePlayfield() => new ManiaEditorPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs
index 2fa3f378ff..c4429176d1 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs
@@ -4,6 +4,7 @@
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Screens.Edit.Compose.Components;
@@ -30,6 +31,6 @@ namespace osu.Game.Rulesets.Mania.Edit
return base.CreateBlueprintFor(hitObject);
}
- protected override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler();
+ protected override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler();
}
}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs b/osu.Game.Rulesets.Mania/Edit/ManiaEditorPlayfield.cs
similarity index 75%
rename from osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs
rename to osu.Game.Rulesets.Mania/Edit/ManiaEditorPlayfield.cs
index a42f793a77..186d50716e 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaEditorPlayfield.cs
@@ -7,9 +7,9 @@ using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania.Edit
{
- public class ManiaEditPlayfield : ManiaPlayfield
+ public class ManiaEditorPlayfield : ManiaPlayfield
{
- public ManiaEditPlayfield(List stages)
+ public ManiaEditorPlayfield(List stages)
: base(stages)
{
}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
index d9570bf8be..2baec95c94 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaHitObjectComposer : HitObjectComposer
{
- private DrawableManiaEditRuleset drawableRuleset;
+ private DrawableManiaEditorRuleset drawableRuleset;
private ManiaBeatSnapGrid beatSnapGrid;
private InputManager inputManager;
@@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Mania.Edit
protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
{
- drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
+ drawableRuleset = new DrawableManiaEditorRuleset(ruleset, beatmap, mods);
// This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it
dependencies.CacheAs(drawableRuleset.ScrollingInfo);
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs
index 2689ed4112..7042110423 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs
@@ -7,12 +7,13 @@ using osu.Framework.Allocation;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit.Compose.Components;
namespace osu.Game.Rulesets.Mania.Edit
{
- public class ManiaSelectionHandler : SelectionHandler
+ public class ManiaSelectionHandler : EditorSelectionHandler
{
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
@@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Edit
[Resolved]
private HitObjectComposer composer { get; set; }
- public override bool HandleMovement(MoveSelectionEvent moveEvent)
+ public override bool HandleMovement(MoveSelectionEvent moveEvent)
{
var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint;
int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column;
@@ -30,11 +31,11 @@ namespace osu.Game.Rulesets.Mania.Edit
return true;
}
- private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
+ private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
{
var maniaPlayfield = ((ManiaHitObjectComposer)composer).Playfield;
- var currentColumn = maniaPlayfield.GetColumnByPosition(moveEvent.ScreenSpacePosition);
+ var currentColumn = maniaPlayfield.GetColumnByPosition(moveEvent.Blueprint.ScreenSpaceSelectionPoint + moveEvent.ScreenSpaceDelta);
if (currentColumn == null)
return;
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 88b63606b9..b3889bc7d3 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -239,6 +239,7 @@ namespace osu.Game.Rulesets.Mania
new ManiaModDualStages(),
new ManiaModMirror(),
new ManiaModDifficultyAdjust(),
+ new ManiaModClassic(),
new ManiaModInvert(),
new ManiaModConstantSpeed()
};
diff --git a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
index de77af8306..1c89d9cd00 100644
--- a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
+++ b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
@@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Mania
Current = config.GetBindable(ManiaRulesetSetting.ScrollTime),
KeyboardStep = 5
},
+ new SettingsCheckbox
+ {
+ LabelText = "Timing-based note colouring",
+ Current = config.GetBindable(ManiaRulesetSetting.TimingBasedNoteColouring),
+ }
};
}
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModClassic.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModClassic.cs
new file mode 100644
index 0000000000..073dda9de8
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModClassic.cs
@@ -0,0 +1,11 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Rulesets.Mods;
+
+namespace osu.Game.Rulesets.Mania.Mods
+{
+ public class ManiaModClassic : ModClassic
+ {
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 828ee7b03e..02829d87bd 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -221,7 +221,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// As the note is being held, adjust the size of the sizing container. This has two effects:
// 1. The contained masking container will mask the body and ticks.
// 2. The head note will move along with the new "head position" in the container.
- if (Head.IsHit && releaseTime == null)
+ if (Head.IsHit && releaseTime == null && DrawHeight > 0)
{
// How far past the hit target this hold note is. Always a positive value.
float yOffset = Math.Max(0, Direction.Value == ScrollingDirection.Up ? -Y : Y);
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index b512986ccb..36565e14aa 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -2,13 +2,19 @@
// See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
+using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Screens.Edit;
using osu.Game.Skinning;
+using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
@@ -17,6 +23,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
public class DrawableNote : DrawableManiaHitObject, IKeyBindingHandler
{
+ [Resolved]
+ private OsuColour colours { get; set; }
+
+ [Resolved(canBeNull: true)]
+ private IBeatmap beatmap { get; set; }
+
+ private readonly Bindable configTimingBasedNoteColouring = new Bindable();
+
protected virtual ManiaSkinComponents Component => ManiaSkinComponents.Note;
private readonly Drawable headPiece;
@@ -34,6 +48,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
});
}
+ [BackgroundDependencyLoader(true)]
+ private void load(ManiaRulesetConfigManager rulesetConfig)
+ {
+ rulesetConfig?.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring);
+ }
+
+ protected override void LoadComplete()
+ {
+ HitObject.StartTimeBindable.BindValueChanged(_ => updateSnapColour());
+ configTimingBasedNoteColouring.BindValueChanged(_ => updateSnapColour(), true);
+ }
+
protected override void OnDirectionChanged(ValueChangedEvent e)
{
base.OnDirectionChanged(e);
@@ -73,5 +99,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public virtual void OnReleased(ManiaAction action)
{
}
+
+ private void updateSnapColour()
+ {
+ if (beatmap == null) return;
+
+ int snapDivisor = beatmap.ControlPointInfo.GetClosestBeatDivisor(HitObject.StartTime);
+
+ Colour = configTimingBasedNoteColouring.Value ? BindableBeatDivisor.GetColourFor(snapDivisor, colours) : Color4.White;
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
index 27bf50493d..6289744df1 100644
--- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
-using osu.Game.Rulesets.Mania.Objects.Types;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs
index f9445a9a96..6139b0e676 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs
@@ -10,6 +10,7 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Edit.Checks;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
+using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
@@ -30,25 +31,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
[Test]
public void TestCircleInCenter()
{
- var beatmap = new Beatmap
+ assertOk(new Beatmap
{
HitObjects = new List
{
new HitCircle
{
StartTime = 3000,
- Position = playfield_centre // Playfield is 640 x 480.
+ Position = playfield_centre
}
}
- };
-
- Assert.That(check.Run(beatmap), Is.Empty);
+ });
}
[Test]
public void TestCircleNearEdge()
{
- var beatmap = new Beatmap
+ assertOk(new Beatmap
{
HitObjects = new List
{
@@ -58,15 +57,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Position = new Vector2(5, 5)
}
}
- };
-
- Assert.That(check.Run(beatmap), Is.Empty);
+ });
}
[Test]
public void TestCircleNearEdgeStackedOffscreen()
{
- var beatmap = new Beatmap
+ assertOffscreenCircle(new Beatmap
{
HitObjects = new List
{
@@ -77,15 +74,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
StackHeight = 5
}
}
- };
-
- assertOffscreenCircle(beatmap);
+ });
}
[Test]
public void TestCircleOffscreen()
{
- var beatmap = new Beatmap
+ assertOffscreenCircle(new Beatmap
{
HitObjects = new List
{
@@ -95,15 +90,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Position = new Vector2(0, 0)
}
}
- };
-
- assertOffscreenCircle(beatmap);
+ });
}
[Test]
public void TestSliderInCenter()
{
- var beatmap = new Beatmap
+ assertOk(new Beatmap
{
HitObjects = new List
{
@@ -118,15 +111,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
}),
}
}
- };
-
- Assert.That(check.Run(beatmap), Is.Empty);
+ });
}
[Test]
public void TestSliderNearEdge()
{
- var beatmap = new Beatmap
+ assertOk(new Beatmap
{
HitObjects = new List
{
@@ -141,15 +132,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
}),
}
}
- };
-
- Assert.That(check.Run(beatmap), Is.Empty);
+ });
}
[Test]
public void TestSliderNearEdgeStackedOffscreen()
{
- var beatmap = new Beatmap
+ assertOffscreenSlider(new Beatmap
{
HitObjects = new List
{
@@ -165,15 +154,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
StackHeight = 5
}
}
- };
-
- assertOffscreenSlider(beatmap);
+ });
}
[Test]
public void TestSliderOffscreenStart()
{
- var beatmap = new Beatmap
+ assertOffscreenSlider(new Beatmap
{
HitObjects = new List
{
@@ -188,15 +175,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
}),
}
}
- };
-
- assertOffscreenSlider(beatmap);
+ });
}
[Test]
public void TestSliderOffscreenEnd()
{
- var beatmap = new Beatmap
+ assertOffscreenSlider(new Beatmap
{
HitObjects = new List
{
@@ -211,15 +196,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
}),
}
}
- };
-
- assertOffscreenSlider(beatmap);
+ });
}
[Test]
public void TestSliderOffscreenPath()
{
- var beatmap = new Beatmap
+ assertOffscreenSlider(new Beatmap
{
HitObjects = new List
{
@@ -236,14 +219,17 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
}),
}
}
- };
+ });
+ }
- assertOffscreenSlider(beatmap);
+ private void assertOk(IBeatmap beatmap)
+ {
+ Assert.That(check.Run(beatmap, new TestWorkingBeatmap(beatmap)), Is.Empty);
}
private void assertOffscreenCircle(IBeatmap beatmap)
{
- var issues = check.Run(beatmap).ToList();
+ var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle);
@@ -251,7 +237,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
private void assertOffscreenSlider(IBeatmap beatmap)
{
- var issues = check.Run(beatmap).ToList();
+ var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList();
Assert.That(issues, Has.Count.EqualTo(1));
Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider);
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleApplication.cs
index 5fc1082743..8b3fead366 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleApplication.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleApplication.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
Position = new Vector2(128, 128),
ComboIndex = 1,
- }), null));
+ })));
}
private HitCircle prepareObject(HitCircle circle)
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs
index aac6db60fe..e698766aac 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests
new Vector2(300, 0),
}),
RepeatCount = 1
- }), null));
+ })));
}
[Test]
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
index 2cc031405e..590d159300 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
@@ -34,6 +34,18 @@ namespace osu.Game.Rulesets.Osu.Tests
private List judgementResults;
+ [Test]
+ public void TestPressBothKeysSimultaneouslyAndReleaseOne()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = Vector2.Zero, Actions = { OsuAction.LeftButton, OsuAction.RightButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = Vector2.Zero, Actions = { OsuAction.RightButton }, Time = time_during_slide_1 },
+ });
+
+ AddAssert("Tracking retained", assertMaxJudge);
+ }
+
///
/// Scenario:
/// - Press a key before a slider starts
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerApplication.cs
index d7fbc7ac48..8c97c02049 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerApplication.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerApplication.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Position = new Vector2(256, 192),
ComboIndex = 1,
Duration = 1000,
- }), null));
+ })));
AddAssert("rotation is reset", () => dho.Result.RateAdjustedRotation == 0);
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
index 14c709cae1..8ff21057b5 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
@@ -21,6 +21,7 @@ using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
+using osu.Game.Screens.Play;
using osu.Game.Storyboards;
using osu.Game.Tests.Visual;
using osuTK;
@@ -193,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Tests
addSeekStep(0);
- AddStep("adjust track rate", () => Player.GameplayClockContainer.UserPlaybackRate.Value = rate);
+ AddStep("adjust track rate", () => ((MasterGameplayClockContainer)Player.GameplayClockContainer).UserPlaybackRate.Value = rate);
addSeekStep(1000);
AddAssert("progress almost same", () => Precision.AlmostEquals(expectedProgress, drawableSpinner.Progress, 0.05));
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index f743d65db3..e01e858873 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -3,7 +3,7 @@
-
+
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs
index 8dd550bb96..299f8fc43a 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs
@@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
public abstract class OsuSelectionBlueprint : OverlaySelectionBlueprint
where T : OsuHitObject
{
- protected new T HitObject => (T)DrawableObject.HitObject;
+ protected T HitObject => (T)DrawableObject.HitObject;
protected override bool AlwaysShowWhenSelected => true;
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
index 77ea3b05dc..8b20df9a68 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
@@ -207,7 +207,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
var lastPiece = controlPointVisualiser.Pieces.Single(p => p.ControlPoint == last);
lastPoint = last;
- return lastPiece?.IsHovered != true;
+ return lastPiece.IsHovered != true;
}
private void updateSlider()
diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs
index 27cae2ecc1..4b0a7531a1 100644
--- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs
@@ -31,9 +31,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks
new IssueTemplateOffscreenSlider(this)
};
- public IEnumerable Run(IBeatmap beatmap)
+ public IEnumerable Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap)
{
- foreach (var hitobject in beatmap.HitObjects)
+ foreach (var hitobject in playableBeatmap.HitObjects)
{
switch (hitobject)
{
diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs
deleted file mode 100644
index 5fdb79cbbd..0000000000
--- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Graphics;
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Osu.UI;
-using osu.Game.Rulesets.UI;
-using osuTK;
-
-namespace osu.Game.Rulesets.Osu.Edit
-{
- public class DrawableOsuEditRuleset : DrawableOsuRuleset
- {
- public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods)
- : base(ruleset, beatmap, mods)
- {
- }
-
- protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
-
- public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { Size = Vector2.One };
-
- private class OsuEditPlayfield : OsuPlayfield
- {
- protected override GameplayCursorContainer CreateCursor() => null;
-
- protected override void OnNewDrawableHitObject(DrawableHitObject d)
- {
- d.ApplyCustomUpdateState += updateState;
- }
-
- ///
- /// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
- /// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
- ///
- private const double editor_hit_object_fade_out_extension = 700;
-
- private void updateState(DrawableHitObject hitObject, ArmedState state)
- {
- if (state == ArmedState.Idle)
- return;
-
- // adjust the visuals of certain object types to make them stay on screen for longer than usual.
- switch (hitObject)
- {
- default:
- // there are quite a few drawable hit types we don't want to extend (spinners, ticks etc.)
- return;
-
- case DrawableSlider _:
- // no specifics to sliders but let them fade slower below.
- break;
-
- case DrawableHitCircle circle: // also handles slider heads
- circle.ApproachCircle
- .FadeOutFromOne(editor_hit_object_fade_out_extension)
- .Expire();
- break;
- }
-
- // Get the existing fade out transform
- var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
-
- if (existing == null)
- return;
-
- hitObject.RemoveTransform(existing);
-
- using (hitObject.BeginAbsoluteSequence(existing.StartTime))
- hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs
new file mode 100644
index 0000000000..aeeae84d14
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs
@@ -0,0 +1,102 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Skinning.Default;
+using osu.Game.Rulesets.Osu.UI;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Edit
+{
+ public class DrawableOsuEditorRuleset : DrawableOsuRuleset
+ {
+ public DrawableOsuEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods)
+ : base(ruleset, beatmap, mods)
+ {
+ }
+
+ protected override Playfield CreatePlayfield() => new OsuEditorPlayfield();
+
+ public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { Size = Vector2.One };
+
+ private class OsuEditorPlayfield : OsuPlayfield
+ {
+ private Bindable hitAnimations;
+
+ protected override GameplayCursorContainer CreateCursor() => null;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config)
+ {
+ hitAnimations = config.GetBindable(OsuSetting.EditorHitAnimations);
+ }
+
+ protected override void OnNewDrawableHitObject(DrawableHitObject d)
+ {
+ d.ApplyCustomUpdateState += updateState;
+ }
+
+ ///
+ /// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
+ /// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
+ ///
+ private const double editor_hit_object_fade_out_extension = 700;
+
+ private void updateState(DrawableHitObject hitObject, ArmedState state)
+ {
+ if (state == ArmedState.Idle || hitAnimations.Value)
+ return;
+
+ if (hitObject is DrawableHitCircle circle)
+ {
+ circle.ApproachCircle
+ .FadeOutFromOne(editor_hit_object_fade_out_extension * 4)
+ .Expire();
+
+ circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint);
+ }
+
+ if (hitObject is IHasMainCirclePiece mainPieceContainer)
+ {
+ // clear any explode animation logic.
+ mainPieceContainer.CirclePiece.ApplyTransformsAt(hitObject.HitStateUpdateTime, true);
+ mainPieceContainer.CirclePiece.ClearTransformsAfter(hitObject.HitStateUpdateTime, true);
+ }
+
+ if (hitObject is DrawableSliderRepeat repeat)
+ {
+ repeat.Arrow.ApplyTransformsAt(hitObject.HitStateUpdateTime, true);
+ repeat.Arrow.ClearTransformsAfter(hitObject.HitStateUpdateTime, true);
+ }
+
+ // adjust the visuals of top-level object types to make them stay on screen for longer than usual.
+ switch (hitObject)
+ {
+ case DrawableSlider _:
+ case DrawableHitCircle _:
+ // Get the existing fade out transform
+ var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
+
+ if (existing == null)
+ return;
+
+ hitObject.RemoveTransform(existing);
+
+ using (hitObject.BeginAbsoluteSequence(hitObject.HitStateUpdateTime))
+ hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs
index 1c7ab00bbb..dab6483179 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs
@@ -17,6 +17,9 @@ namespace osu.Game.Rulesets.Osu.Edit
new CheckOffscreenObjects()
};
- public IEnumerable Run(IBeatmap beatmap) => checks.SelectMany(check => check.Run(beatmap));
+ public IEnumerable Run(IBeatmap playableBeatmap, WorkingBeatmap workingBeatmap)
+ {
+ return checks.SelectMany(check => check.Run(playableBeatmap, workingBeatmap));
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
index a68ed34e6b..abac5eb56e 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
@@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
- protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
+ protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index 396fd41377..806b7e6051 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit
}
protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
- => new DrawableOsuEditRuleset(ruleset, beatmap, mods);
+ => new DrawableOsuEditorRuleset(ruleset, beatmap, mods);
protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[]
{
@@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.Edit
if (b.IsSelected)
continue;
- var hitObject = (OsuHitObject)b.HitObject;
+ var hitObject = (OsuHitObject)b.Item;
Vector2? snap = checkSnap(hitObject.Position);
if (snap == null && hitObject.Position != hitObject.EndPosition)
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs
index de0a4682a3..c2c1f6d602 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs
@@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Utils;
+using osu.Game.Extensions;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
@@ -15,7 +16,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Edit
{
- public class OsuSelectionHandler : SelectionHandler
+ public class OsuSelectionHandler : EditorSelectionHandler
{
protected override void OnSelectionChanged()
{
@@ -36,13 +37,13 @@ namespace osu.Game.Rulesets.Osu.Edit
referencePathTypes = null;
}
- public override bool HandleMovement(MoveSelectionEvent moveEvent)
+ public override bool HandleMovement(MoveSelectionEvent moveEvent)
{
var hitObjects = selectedMovableObjects;
// this will potentially move the selection out of bounds...
foreach (var h in hitObjects)
- h.Position += moveEvent.InstantDelta;
+ h.Position += this.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta);
// but this will be corrected.
moveSelectionInBounds();
@@ -374,8 +375,7 @@ namespace osu.Game.Rulesets.Osu.Edit
///
/// All osu! hitobjects which can be moved/rotated/scaled.
///
- private OsuHitObject[] selectedMovableObjects => EditorBeatmap.SelectedHitObjects
- .OfType()
+ private OsuHitObject[] selectedMovableObjects => SelectedItems.OfType()
.Where(h => !(h is Spinner))
.ToArray();
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs
index aee431284e..9ae9653e9b 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs
@@ -1,47 +1,30 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Bindables;
-using osu.Framework.Extensions;
-using osu.Framework.Graphics;
-using osu.Game.Configuration;
+using System.Collections.Generic;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.UI;
-using osu.Game.Rulesets.UI;
-using osuTK;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods
{
- public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset
+ public class OsuModBarrelRoll : ModBarrelRoll, IApplicableToDrawableHitObjects
{
- [SettingSource("Roll speed", "Rotations per minute")]
- public BindableNumber SpinSpeed { get; } = new BindableDouble(0.5)
+ public void ApplyToDrawableHitObjects(IEnumerable drawables)
{
- MinValue = 0.02,
- MaxValue = 4,
- Precision = 0.01,
- };
-
- [SettingSource("Direction", "The direction of rotation")]
- public Bindable Direction { get; } = new Bindable(RotationDirection.Clockwise);
-
- public override string Name => "Barrel Roll";
- public override string Acronym => "BR";
- public override string Description => "The whole playfield is on a wheel!";
- public override double ScoreMultiplier => 1;
-
- public override string SettingDescription => $"{SpinSpeed.Value} rpm {Direction.Value.GetDescription().ToLowerInvariant()}";
-
- public void Update(Playfield playfield)
- {
- playfield.Rotation = (Direction.Value == RotationDirection.Counterclockwise ? -1 : 1) * 360 * (float)(playfield.Time.Current / 60000 * SpinSpeed.Value);
- }
-
- public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
- {
- // scale the playfield to allow all hitobjects to stay within the visible region.
- drawableRuleset.Playfield.Scale = new Vector2(OsuPlayfield.BASE_SIZE.Y / OsuPlayfield.BASE_SIZE.X);
+ foreach (var d in drawables)
+ {
+ d.OnUpdate += _ =>
+ {
+ switch (d)
+ {
+ case DrawableHitCircle circle:
+ circle.CirclePiece.Rotation = -CurrentRotation;
+ break;
+ }
+ };
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs
index 882f848190..77dea5b0dc 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs
@@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
@@ -16,22 +15,8 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.Mods
{
- public class OsuModClassic : Mod, IApplicableToHitObject, IApplicableToDrawableHitObjects, IApplicableToDrawableRuleset
+ public class OsuModClassic : ModClassic, IApplicableToHitObject, IApplicableToDrawableHitObjects, IApplicableToDrawableRuleset
{
- public override string Name => "Classic";
-
- public override string Acronym => "CL";
-
- public override double ScoreMultiplier => 1;
-
- public override IconUsage? Icon => FontAwesome.Solid.History;
-
- public override string Description => "Feeling nostalgic?";
-
- public override bool Ranked => false;
-
- public override ModType Type => ModType.Conversion;
-
[SettingSource("No slider head accuracy requirement", "Scores sliders proportionally to the number of ticks hit.")]
public Bindable NoSliderHeadAccuracy { get; } = new BindableBool(true);
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
index 9c5e41f245..a01cec4bb3 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
@@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Osu.Mods
var osuObject = (OsuHitObject)drawable.HitObject;
Vector2 origin = drawable.Position;
- // Wiggle the repeat points with the slider instead of independently.
+ // Wiggle the repeat points and the tail with the slider instead of independently.
// Also fixes an issue with repeat points being positioned incorrectly.
- if (osuObject is SliderRepeat)
+ if (osuObject is SliderRepeat || osuObject is SliderTailCircle)
return;
Random objRand = new Random((int)osuObject.StartTime);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
index 5541d0e790..cda4715280 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Entry = null;
}
- private void onEntryInvalidated() => refreshPoints();
+ private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints);
private void refreshPoints()
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 189003875d..1bf9e76d7d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -19,7 +19,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
- public class DrawableHitCircle : DrawableOsuHitObject
+ public class DrawableHitCircle : DrawableOsuHitObject, IHasMainCirclePiece
{
public OsuAction? HitAction => HitArea.HitAction;
protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle;
@@ -66,7 +66,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return true;
},
},
- CirclePiece = new SkinnableDrawable(new OsuSkinComponent(CirclePieceComponent), _ => new MainCirclePiece()),
+ CirclePiece = new SkinnableDrawable(new OsuSkinComponent(CirclePieceComponent), _ => new MainCirclePiece())
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
ApproachCircle = new ApproachCircle
{
Alpha = 0,
@@ -178,6 +182,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// todo: temporary / arbitrary, used for lifetime optimisation.
this.Delay(800).FadeOut();
+ (CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
+
switch (state)
{
case ArmedState.Idle:
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
index 76490e0de1..7b4188edab 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
@@ -15,7 +15,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
- public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking
+ public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking, IHasMainCirclePiece
{
public new SliderRepeat HitObject => (SliderRepeat)base.HitObject;
@@ -26,9 +26,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private double animDuration;
- public Drawable CirclePiece { get; private set; }
+ public SkinnableDrawable CirclePiece { get; private set; }
+
+ public ReverseArrowPiece Arrow { get; private set; }
+
private Drawable scaleContainer;
- private ReverseArrowPiece arrow;
public override bool DisplayResult => false;
@@ -53,11 +55,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Children = new[]
+ Children = new Drawable[]
{
// no default for this; only visible in legacy skins.
- CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty()),
- arrow = new ReverseArrowPiece(),
+ CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty())
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ Arrow = new ReverseArrowPiece(),
}
};
@@ -91,6 +97,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdateHitStateTransforms(state);
+ (CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
+
switch (state)
{
case ArmedState.Idle:
@@ -102,8 +110,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break;
case ArmedState.Hit:
- this.FadeOut(animDuration, Easing.Out)
- .ScaleTo(Scale * 1.5f, animDuration, Easing.Out);
+ this.FadeOut(animDuration, Easing.Out);
+
+ const float final_scale = 1.5f;
+
+ Arrow.ScaleTo(Scale * final_scale, animDuration, Easing.Out);
+ CirclePiece.ScaleTo(Scale * final_scale, animDuration, Easing.Out);
break;
}
}
@@ -139,18 +151,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
float aimRotation = MathUtils.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
- while (Math.Abs(aimRotation - arrow.Rotation) > 180)
- aimRotation += aimRotation < arrow.Rotation ? 360 : -360;
+ while (Math.Abs(aimRotation - Arrow.Rotation) > 180)
+ aimRotation += aimRotation < Arrow.Rotation ? 360 : -360;
if (!hasRotation)
{
- arrow.Rotation = aimRotation;
+ Arrow.Rotation = aimRotation;
hasRotation = true;
}
else
{
// If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
- arrow.Rotation = Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, 100), arrow.Rotation, aimRotation, 0, 50, Easing.OutQuint);
+ Arrow.Rotation = Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, 100), Arrow.Rotation, aimRotation, 0, 50, Easing.OutQuint);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
index 87f098dd29..d81af053d1 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
@@ -7,12 +7,13 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
- public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking
+ public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking, IHasMainCirclePiece
{
public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject;
@@ -34,7 +35,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking { get; set; }
- private SkinnableDrawable circlePiece;
+ public SkinnableDrawable CirclePiece { get; private set; }
+
private Container scaleContainer;
public DrawableSliderTail()
@@ -63,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Children = new Drawable[]
{
// no default for this; only visible in legacy skins.
- circlePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty())
+ CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty())
}
},
};
@@ -75,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdateInitialTransforms();
- circlePiece.FadeInFromZero(HitObject.TimeFadeIn);
+ CirclePiece.FadeInFromZero(HitObject.TimeFadeIn);
}
protected override void UpdateHitStateTransforms(ArmedState state)
@@ -84,6 +86,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Debug.Assert(HitObject.HitWindows != null);
+ (CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
+
switch (state)
{
case ArmedState.Idle:
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/IHasMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/IHasMainCirclePiece.cs
new file mode 100644
index 0000000000..8bb7629542
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/IHasMainCirclePiece.cs
@@ -0,0 +1,12 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Osu.Skinning.Default
+{
+ public interface IHasMainCirclePiece
+ {
+ SkinnableDrawable CirclePiece { get; }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs
new file mode 100644
index 0000000000..17a1e29094
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs
@@ -0,0 +1,17 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Osu.Skinning.Default
+{
+ public interface IMainCirclePiece
+ {
+ ///
+ /// Begins animating this .
+ ///
+ /// The of the related .
+ void Animate(ArmedState state);
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs
index 46aeadc59b..b46baa00ba 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs
@@ -13,7 +13,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Skinning.Default
{
- public class MainCirclePiece : CompositeDrawable
+ public class MainCirclePiece : CompositeDrawable, IMainCirclePiece
{
private readonly CirclePiece circle;
private readonly RingPiece ring;
@@ -67,12 +67,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
}, true);
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
-
- drawableObject.ApplyCustomUpdateState += updateState;
- updateState(drawableObject, drawableObject.State.Value);
}
- private void updateState(DrawableHitObject drawableObject, ArmedState state)
+ public void Animate(ArmedState state)
{
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
glow.FadeOut(400);
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs
index 82b677e12c..8feeca56e8 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -134,6 +135,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
///
private double? timeToAcceptAnyKeyAfter;
+ ///
+ /// The actions that were pressed in the previous frame.
+ ///
+ private readonly List lastPressedActions = new List();
+
protected override void Update()
{
base.Update();
@@ -152,8 +158,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
{
var otherKey = headCircleHitAction == OsuAction.RightButton ? OsuAction.LeftButton : OsuAction.RightButton;
- // we can return to accepting all keys if the initial head circle key is the *only* key pressed, or all keys have been released.
- if (actions?.Contains(otherKey) != true)
+ // we can start accepting any key once all other keys have been released in the previous frame.
+ if (!lastPressedActions.Contains(otherKey))
timeToAcceptAnyKeyAfter = Time.Current;
}
@@ -164,6 +170,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
// valid action
(actions?.Any(isValidTrackingAction) ?? false);
+
+ lastPressedActions.Clear();
+ if (actions != null)
+ lastPressedActions.AddRange(actions);
}
///
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs
index 545e80a709..cf62165929 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs
@@ -12,6 +12,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
@@ -19,7 +20,7 @@ using static osu.Game.Skinning.LegacySkinConfiguration;
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
- public class LegacyMainCirclePiece : CompositeDrawable
+ public class LegacyMainCirclePiece : CompositeDrawable, IMainCirclePiece
{
private readonly string priorityLookup;
private readonly bool hasNumber;
@@ -138,12 +139,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
if (hasNumber)
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
-
- drawableObject.ApplyCustomUpdateState += updateState;
- updateState(drawableObject, drawableObject.State.Value);
}
- private void updateState(DrawableHitObject drawableObject, ArmedState state)
+ public void Animate(ArmedState state)
{
const double legacy_fade_duration = 240;
diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
index 44ca5e850f..27d48d1296 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.UI
base.PopIn();
GameplayCursor.ActiveCursor.Hide();
- cursorScaleContainer.MoveTo(GameplayCursor.ActiveCursor.Position);
+ cursorScaleContainer.Position = ToLocalSpace(GameplayCursor.ActiveCursor.ScreenSpaceDrawQuad.Centre);
clickToResumeCursor.Appear();
if (localCursorContainer == null)
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs
index a970965141..f33c738b04 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
StartTime = 400,
Major = true
- }), null));
+ })));
AddHitObject(barLine);
RemoveHitObject(barLine);
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
StartTime = 200,
Major = false
- }), null));
+ })));
AddHitObject(barLine);
}
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumRollApplication.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumRollApplication.cs
index 54450e27db..c389a05566 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumRollApplication.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumRollApplication.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Duration = 500,
IsStrong = false,
TickRate = 2
- }), null));
+ })));
AddHitObject(drumRoll);
RemoveHitObject(drumRoll);
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Duration = 400,
IsStrong = true,
TickRate = 16
- }), null));
+ })));
AddHitObject(drumRoll);
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHitApplication.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHitApplication.cs
index 52fd440857..c2f251fcb6 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHitApplication.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHitApplication.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Type = HitType.Rim,
IsStrong = false,
StartTime = 300
- }), null));
+ })));
AddHitObject(hit);
RemoveHitObject(hit);
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Type = HitType.Centre,
IsStrong = true,
StartTime = 500
- }), null));
+ })));
AddHitObject(hit);
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
index eab144592f..2dfa1dfbb7 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -3,7 +3,7 @@
-
+
diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs
index 8b41448c9d..b1b08a9461 100644
--- a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Edit.Blueprints;
using osu.Game.Screens.Edit.Compose.Components;
@@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Edit
{
}
- protected override SelectionHandler CreateSelectionHandler() => new TaikoSelectionHandler();
+ protected override SelectionHandler CreateSelectionHandler() => new TaikoSelectionHandler();
public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) =>
new TaikoSelectionBlueprint(hitObject);
diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs
index ac2dd4bdb6..48ee0d4cf4 100644
--- a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs
+++ b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs
@@ -8,12 +8,13 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Edit.Compose.Components;
namespace osu.Game.Rulesets.Taiko.Edit
{
- public class TaikoSelectionHandler : SelectionHandler
+ public class TaikoSelectionHandler : EditorSelectionHandler
{
private readonly Bindable selectionRimState = new Bindable();
private readonly Bindable selectionStrongState = new Bindable();
@@ -72,16 +73,19 @@ namespace osu.Game.Rulesets.Taiko.Edit
});
}
- protected override IEnumerable