diff --git a/osu.Android.props b/osu.Android.props
index 9a3d42d6b7..eaedcb7bc3 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index adf9c452f6..8b8ad9f8af 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -29,7 +29,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs
index b570f090ca..e70def7f8b 100644
--- a/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs
+++ b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs
@@ -4,6 +4,7 @@
using NUnit.Framework;
using osu.Framework.IO.Stores;
using osu.Game.Rulesets.Catch.Skinning;
+using osu.Game.Rulesets.Catch.Skinning.Legacy;
using osu.Game.Skinning;
using osuTK.Graphics;
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
index 194a12a9b7..e8bb57cdf3 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -1,6 +1,7 @@
// 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.Collections.Generic;
using System.Linq;
using NUnit.Framework;
@@ -12,8 +13,12 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration;
+using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Skinning;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
@@ -24,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[Resolved]
private OsuConfigManager config { get; set; }
- private Container droppedObjectContainer;
+ private Container droppedObjectContainer;
private TestCatcher catcher;
@@ -37,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Tests
};
var trailContainer = new Container();
- droppedObjectContainer = new Container();
+ droppedObjectContainer = new Container();
catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty);
Child = new Container
@@ -52,6 +57,50 @@ namespace osu.Game.Rulesets.Catch.Tests
};
});
+ [Test]
+ public void TestCatcherHyperStateReverted()
+ {
+ DrawableCatchHitObject drawableObject1 = null;
+ DrawableCatchHitObject drawableObject2 = null;
+ JudgementResult result1 = null;
+ JudgementResult result2 = null;
+ AddStep("catch hyper fruit", () =>
+ {
+ attemptCatch(new Fruit { HyperDashTarget = new Fruit { X = 100 } }, out drawableObject1, out result1);
+ });
+ AddStep("catch normal fruit", () =>
+ {
+ attemptCatch(new Fruit(), out drawableObject2, out result2);
+ });
+ AddStep("revert second result", () =>
+ {
+ catcher.OnRevertResult(drawableObject2, result2);
+ });
+ checkHyperDash(true);
+ AddStep("revert first result", () =>
+ {
+ catcher.OnRevertResult(drawableObject1, result1);
+ });
+ checkHyperDash(false);
+ }
+
+ [Test]
+ public void TestCatcherAnimationStateReverted()
+ {
+ DrawableCatchHitObject drawableObject = null;
+ JudgementResult result = null;
+ AddStep("catch kiai fruit", () =>
+ {
+ attemptCatch(new TestKiaiFruit(), out drawableObject, out result);
+ });
+ checkState(CatcherAnimationState.Kiai);
+ AddStep("revert result", () =>
+ {
+ catcher.OnRevertResult(drawableObject, result);
+ });
+ checkState(CatcherAnimationState.Idle);
+ }
+
[Test]
public void TestCatcherCatchWidth()
{
@@ -149,13 +198,22 @@ namespace osu.Game.Rulesets.Catch.Tests
AddAssert("fruits are dropped", () => !catcher.CaughtObjects.Any() && droppedObjectContainer.Count == 10);
}
- [TestCase(true)]
- [TestCase(false)]
- public void TestHitLighting(bool enabled)
+ [Test]
+ public void TestHitLightingColour()
{
- AddStep($"{(enabled ? "enable" : "disable")} hit lighting", () => config.Set(OsuSetting.HitLighting, enabled));
+ var fruitColour = SkinConfiguration.DefaultComboColours[1];
+ AddStep("enable hit lighting", () => config.Set(OsuSetting.HitLighting, true));
AddStep("catch fruit", () => attemptCatch(new Fruit()));
- AddAssert("check hit lighting", () => catcher.ChildrenOfType().Any() == enabled);
+ AddAssert("correct hit lighting colour", () =>
+ catcher.ChildrenOfType().First()?.ObjectColour == fruitColour);
+ }
+
+ [Test]
+ public void TestHitLightingDisabled()
+ {
+ AddStep("disable hit lighting", () => config.Set(OsuSetting.HitLighting, false));
+ AddStep("catch fruit", () => attemptCatch(new Fruit()));
+ AddAssert("no hit lighting", () => !catcher.ChildrenOfType().Any());
}
private void checkPlate(int count) => AddAssert($"{count} objects on the plate", () => catcher.CaughtObjects.Count() == count);
@@ -166,17 +224,60 @@ namespace osu.Game.Rulesets.Catch.Tests
private void attemptCatch(CatchHitObject hitObject, int count = 1)
{
- hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
-
for (var i = 0; i < count; i++)
- catcher.AttemptCatch(hitObject);
+ attemptCatch(hitObject, out _, out _);
+ }
+
+ private void attemptCatch(CatchHitObject hitObject, out DrawableCatchHitObject drawableObject, out JudgementResult result)
+ {
+ hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+ drawableObject = createDrawableObject(hitObject);
+ result = createResult(hitObject);
+ applyResult(drawableObject, result);
+ }
+
+ private void applyResult(DrawableCatchHitObject drawableObject, JudgementResult result)
+ {
+ // Load DHO to set colour of hit explosion correctly
+ Add(drawableObject);
+ drawableObject.OnLoadComplete += _ =>
+ {
+ catcher.OnNewResult(drawableObject, result);
+ drawableObject.Expire();
+ };
+ }
+
+ private JudgementResult createResult(CatchHitObject hitObject)
+ {
+ return new CatchJudgementResult(hitObject, hitObject.CreateJudgement())
+ {
+ Type = catcher.CanCatch(hitObject) ? HitResult.Great : HitResult.Miss
+ };
+ }
+
+ private DrawableCatchHitObject createDrawableObject(CatchHitObject hitObject)
+ {
+ switch (hitObject)
+ {
+ case Banana banana:
+ return new DrawableBanana(banana);
+
+ case Droplet droplet:
+ return new DrawableDroplet(droplet);
+
+ case Fruit fruit:
+ return new DrawableFruit(fruit);
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(hitObject));
+ }
}
public class TestCatcher : Catcher
{
- public IEnumerable CaughtObjects => this.ChildrenOfType();
+ public IEnumerable CaughtObjects => this.ChildrenOfType();
- public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty)
+ public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty)
: base(trailsTarget, droppedObjectTarget, difficulty)
{
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
index 281ddc7eaa..31c285ef22 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
@@ -15,7 +15,6 @@ using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI;
-using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Tests
@@ -58,10 +57,9 @@ namespace osu.Game.Rulesets.Catch.Tests
Schedule(() =>
{
- bool caught = area.AttemptCatch(fruit);
- area.OnNewResult(drawable, new JudgementResult(fruit, new CatchJudgement())
+ area.OnNewResult(drawable, new CatchJudgementResult(fruit, new CatchJudgement())
{
- Type = caught ? HitResult.Great : HitResult.Miss
+ Type = area.MovableCatcher.CanCatch(fruit) ? HitResult.Great : HitResult.Miss
});
drawable.Expire();
@@ -75,7 +73,10 @@ namespace osu.Game.Rulesets.Catch.Tests
SetContents(() =>
{
- var droppedObjectContainer = new Container();
+ var droppedObjectContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both
+ };
return new CatchInputManager(catchRuleset)
{
@@ -101,7 +102,7 @@ namespace osu.Game.Rulesets.Catch.Tests
private class TestCatcherArea : CatcherArea
{
- public TestCatcherArea(Container droppedObjectContainer, BeatmapDifficulty beatmapDifficulty)
+ public TestCatcherArea(Container droppedObjectContainer, BeatmapDifficulty beatmapDifficulty)
: base(droppedObjectContainer, beatmapDifficulty)
{
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs
index 2ffebb7de1..c888dc0a65 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs
@@ -5,19 +5,20 @@ using NUnit.Framework;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Tests.Visual;
+using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Tests
{
public class TestSceneFruitRandomness : OsuTestScene
{
- private readonly TestDrawableFruit drawableFruit;
- private readonly TestDrawableBanana drawableBanana;
+ private readonly DrawableFruit drawableFruit;
+ private readonly DrawableBanana drawableBanana;
public TestSceneFruitRandomness()
{
- drawableFruit = new TestDrawableFruit(new Fruit());
- drawableBanana = new TestDrawableBanana(new Banana());
+ drawableFruit = new DrawableFruit(new Fruit());
+ drawableBanana = new DrawableBanana(new Banana());
Add(new TestDrawableCatchHitObjectSpecimen(drawableFruit) { X = -200 });
Add(new TestDrawableCatchHitObjectSpecimen(drawableBanana));
@@ -37,16 +38,16 @@ namespace osu.Game.Rulesets.Catch.Tests
float fruitRotation = 0;
float bananaRotation = 0;
- float bananaScale = 0;
+ Vector2 bananaSize = new Vector2();
Color4 bananaColour = new Color4();
AddStep("Initialize start time", () =>
{
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time;
- fruitRotation = drawableFruit.InnerRotation;
- bananaRotation = drawableBanana.InnerRotation;
- bananaScale = drawableBanana.InnerScale;
+ fruitRotation = drawableFruit.DisplayRotation;
+ bananaRotation = drawableBanana.DisplayRotation;
+ bananaSize = drawableBanana.DisplaySize;
bananaColour = drawableBanana.AccentColour.Value;
});
@@ -55,9 +56,9 @@ namespace osu.Game.Rulesets.Catch.Tests
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = another_start_time;
});
- AddAssert("fruit rotation is changed", () => drawableFruit.InnerRotation != fruitRotation);
- AddAssert("banana rotation is changed", () => drawableBanana.InnerRotation != bananaRotation);
- AddAssert("banana scale is changed", () => drawableBanana.InnerScale != bananaScale);
+ AddAssert("fruit rotation is changed", () => drawableFruit.DisplayRotation != fruitRotation);
+ AddAssert("banana rotation is changed", () => drawableBanana.DisplayRotation != bananaRotation);
+ AddAssert("banana size is changed", () => drawableBanana.DisplaySize != bananaSize);
AddAssert("banana colour is changed", () => drawableBanana.AccentColour.Value != bananaColour);
AddStep("reset start time", () =>
@@ -65,32 +66,11 @@ namespace osu.Game.Rulesets.Catch.Tests
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time;
});
- AddAssert("rotation and scale restored", () =>
- drawableFruit.InnerRotation == fruitRotation &&
- drawableBanana.InnerRotation == bananaRotation &&
- drawableBanana.InnerScale == bananaScale &&
+ AddAssert("rotation and size restored", () =>
+ drawableFruit.DisplayRotation == fruitRotation &&
+ drawableBanana.DisplayRotation == bananaRotation &&
+ drawableBanana.DisplaySize == bananaSize &&
drawableBanana.AccentColour.Value == bananaColour);
}
-
- private class TestDrawableFruit : DrawableFruit
- {
- public float InnerRotation => ScaleContainer.Rotation;
-
- public TestDrawableFruit(Fruit h)
- : base(h)
- {
- }
- }
-
- private class TestDrawableBanana : DrawableBanana
- {
- public float InnerRotation => ScaleContainer.Rotation;
- public float InnerScale => ScaleContainer.Scale.X;
-
- public TestDrawableBanana(Banana h)
- : base(h)
- {
- }
- }
}
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs
index 07cb73e5ff..683a776dcc 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs
@@ -13,6 +13,7 @@ using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Skinning;
+using osu.Game.Rulesets.Catch.Skinning.Legacy;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
@@ -117,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.Tests
AddStep("create hyper-dashing catcher", () =>
{
- Child = setupSkinHierarchy(catcherArea = new CatcherArea(new Container())
+ Child = setupSkinHierarchy(catcherArea = new CatcherArea(new Container())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index ad584d3f48..0a817eca0d 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -21,7 +21,7 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using System;
-using osu.Game.Rulesets.Catch.Skinning;
+using osu.Game.Rulesets.Catch.Skinning.Legacy;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch
diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs
index 23d8428fec..668f7197be 100644
--- a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs
+++ b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs
@@ -5,11 +5,8 @@ namespace osu.Game.Rulesets.Catch
{
public enum CatchSkinComponents
{
- FruitBananas,
- FruitApple,
- FruitGrapes,
- FruitOrange,
- FruitPear,
+ Fruit,
+ Banana,
Droplet,
CatcherIdle,
CatcherFail,
diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchJudgementResult.cs b/osu.Game.Rulesets.Catch/Judgements/CatchJudgementResult.cs
new file mode 100644
index 0000000000..c09355d59c
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Judgements/CatchJudgementResult.cs
@@ -0,0 +1,28 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using JetBrains.Annotations;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Objects;
+
+namespace osu.Game.Rulesets.Catch.Judgements
+{
+ public class CatchJudgementResult : JudgementResult
+ {
+ ///
+ /// The catcher animation state prior to this judgement.
+ ///
+ public CatcherAnimationState CatcherAnimationState;
+
+ ///
+ /// Whether the catcher was hyper dashing prior to this judgement.
+ ///
+ public bool CatcherHyperDash;
+
+ public CatchJudgementResult([NotNull] HitObject hitObject, [NotNull] Judgement judgement)
+ : base(hitObject, judgement)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Banana.cs b/osu.Game.Rulesets.Catch/Objects/Banana.cs
index 3f71da713e..178306b3bc 100644
--- a/osu.Game.Rulesets.Catch/Objects/Banana.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Banana.cs
@@ -14,7 +14,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects
{
- public class Banana : Fruit, IHasComboInformation
+ public class Banana : PalpableCatchHitObject, IHasComboInformation
{
///
/// Index of banana in current shower.
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs
new file mode 100644
index 0000000000..8a91f82437
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs
@@ -0,0 +1,18 @@
+// 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.Catch.Skinning.Default;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ ///
+ /// Represents a caught by the catcher.
+ ///
+ public class CaughtBanana : CaughtObject
+ {
+ public CaughtBanana()
+ : base(CatchSkinComponents.Banana, _ => new BananaPiece())
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs
new file mode 100644
index 0000000000..4a3397feff
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs
@@ -0,0 +1,20 @@
+// 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.Catch.Skinning.Default;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ ///
+ /// Represents a caught by the catcher.
+ ///
+ public class CaughtDroplet : CaughtObject
+ {
+ public override bool StaysOnPlate => false;
+
+ public CaughtDroplet()
+ : base(CatchSkinComponents.Droplet, _ => new DropletPiece())
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs
new file mode 100644
index 0000000000..140b411c88
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.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.Bindables;
+using osu.Game.Rulesets.Catch.Skinning.Default;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ ///
+ /// Represents a caught by the catcher.
+ ///
+ public class CaughtFruit : CaughtObject, IHasFruitState
+ {
+ public Bindable VisualRepresentation { get; } = new Bindable();
+
+ public CaughtFruit()
+ : base(CatchSkinComponents.Fruit, _ => new FruitPiece())
+ {
+ }
+
+ public override void CopyStateFrom(IHasCatchObjectState objectState)
+ {
+ base.CopyStateFrom(objectState);
+
+ var fruitState = (IHasFruitState)objectState;
+ VisualRepresentation.Value = fruitState.VisualRepresentation.Value;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs
new file mode 100644
index 0000000000..524505d588
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs
@@ -0,0 +1,64 @@
+// 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 osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Skinning;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ ///
+ /// Represents a caught by the catcher.
+ ///
+ [Cached(typeof(IHasCatchObjectState))]
+ public abstract class CaughtObject : SkinnableDrawable, IHasCatchObjectState
+ {
+ public PalpableCatchHitObject HitObject { get; private set; }
+ public Bindable AccentColour { get; } = new Bindable();
+ public Bindable HyperDash { get; } = new Bindable();
+
+ public Vector2 DisplaySize => Size * Scale;
+
+ public float DisplayRotation => Rotation;
+
+ ///
+ /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher.
+ ///
+ public virtual bool StaysOnPlate => true;
+
+ public override bool RemoveWhenNotAlive => true;
+
+ protected CaughtObject(CatchSkinComponents skinComponent, Func defaultImplementation)
+ : base(new CatchSkinComponent(skinComponent), defaultImplementation)
+ {
+ Origin = Anchor.Centre;
+
+ RelativeSizeAxes = Axes.None;
+ Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
+ }
+
+ ///
+ /// Copies the hit object visual state from another object.
+ ///
+ public virtual void CopyStateFrom(IHasCatchObjectState objectState)
+ {
+ HitObject = objectState.HitObject;
+ Scale = Vector2.Divide(objectState.DisplaySize, Size);
+ Rotation = objectState.DisplayRotation;
+ AccentColour.Value = objectState.AccentColour.Value;
+ HyperDash.Value = objectState.HyperDash.Value;
+ }
+
+ protected override void FreeAfterUse()
+ {
+ ClearTransforms();
+ Alpha = 1;
+
+ base.FreeAfterUse();
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs
index 8e9d80106b..c1b41a7afc 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs
@@ -2,14 +2,15 @@
// See the LICENCE file in the repository root for full licence text.
using JetBrains.Annotations;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Game.Rulesets.Catch.Skinning.Default;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
- public class DrawableBanana : DrawableFruit
+ public class DrawableBanana : DrawablePalpableCatchHitObject
{
- protected override FruitVisualRepresentation GetVisualRepresentation(int indexInBeatmap) => FruitVisualRepresentation.Banana;
-
public DrawableBanana()
: this(null)
{
@@ -20,6 +21,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
}
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ ScalingContainer.Child = new SkinnableDrawable(
+ new CatchSkinComponent(CatchSkinComponents.Banana),
+ _ => new BananaPiece());
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
@@ -35,12 +44,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
const float end_scale = 0.6f;
const float random_scale_range = 1.6f;
- ScaleContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3)))
- .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt);
+ ScalingContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3)))
+ .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt);
- ScaleContainer.RotateTo(getRandomAngle(1))
- .Then()
- .RotateTo(getRandomAngle(2), HitObject.TimePreempt);
+ ScalingContainer.RotateTo(getRandomAngle(1))
+ .Then()
+ .RotateTo(getRandomAngle(2), HitObject.TimePreempt);
float getRandomAngle(int series) => 180 * (RandomSingle(series) * 2 - 1);
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs
index 6aa8ff439e..bfd124c691 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs
@@ -5,7 +5,9 @@ using System;
using JetBrains.Annotations;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Utils;
@@ -48,9 +50,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
public Func CheckPosition;
- public bool IsOnPlate;
-
- public override bool RemoveWhenNotAlive => IsOnPlate;
+ protected override JudgementResult CreateResult(Judgement judgement) => new CatchJudgementResult(HitObject, judgement);
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs
index b8acea625b..2dce9507a5 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs
@@ -4,15 +4,13 @@
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Catch.Skinning.Default;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
public class DrawableDroplet : DrawablePalpableCatchHitObject
{
- public override bool StaysOnPlate => false;
-
public DrawableDroplet()
: this(null)
{
@@ -26,17 +24,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
[BackgroundDependencyLoader]
private void load()
{
- HyperDash.BindValueChanged(_ => updatePiece(), true);
- }
-
- private void updatePiece()
- {
- ScaleContainer.Child = new SkinnableDrawable(
+ ScalingContainer.Child = new SkinnableDrawable(
new CatchSkinComponent(CatchSkinComponents.Droplet),
- _ => new DropletPiece
- {
- HyperDash = { BindTarget = HyperDash }
- });
+ _ => new DropletPiece());
}
protected override void UpdateInitialTransforms()
@@ -47,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
float startRotation = RandomSingle(1) * 20;
double duration = HitObject.TimePreempt + 2000;
- ScaleContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration);
+ ScalingContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration);
}
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs
index ef9df02a68..0b89c46480 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs
@@ -1,21 +1,18 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Catch.Skinning.Default;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
- public class DrawableFruit : DrawablePalpableCatchHitObject
+ public class DrawableFruit : DrawablePalpableCatchHitObject, IHasFruitState
{
- public readonly Bindable VisualRepresentation = new Bindable();
-
- protected virtual FruitVisualRepresentation GetVisualRepresentation(int indexInBeatmap) => (FruitVisualRepresentation)(indexInBeatmap % 4);
+ public Bindable VisualRepresentation { get; } = new Bindable();
public DrawableFruit()
: this(null)
@@ -32,53 +29,19 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
IndexInBeatmap.BindValueChanged(change =>
{
- VisualRepresentation.Value = GetVisualRepresentation(change.NewValue);
+ VisualRepresentation.Value = (FruitVisualRepresentation)(change.NewValue % 4);
}, true);
- VisualRepresentation.BindValueChanged(_ => updatePiece());
- HyperDash.BindValueChanged(_ => updatePiece(), true);
+ ScalingContainer.Child = new SkinnableDrawable(
+ new CatchSkinComponent(CatchSkinComponents.Fruit),
+ _ => new FruitPiece());
}
protected override void UpdateInitialTransforms()
{
base.UpdateInitialTransforms();
- ScaleContainer.RotateTo((RandomSingle(1) - 0.5f) * 40);
- }
-
- private void updatePiece()
- {
- ScaleContainer.Child = new SkinnableDrawable(
- new CatchSkinComponent(getComponent(VisualRepresentation.Value)),
- _ => new FruitPiece
- {
- VisualRepresentation = { BindTarget = VisualRepresentation },
- HyperDash = { BindTarget = HyperDash },
- });
- }
-
- private CatchSkinComponents getComponent(FruitVisualRepresentation hitObjectVisualRepresentation)
- {
- switch (hitObjectVisualRepresentation)
- {
- case FruitVisualRepresentation.Pear:
- return CatchSkinComponents.FruitPear;
-
- case FruitVisualRepresentation.Grape:
- return CatchSkinComponents.FruitGrapes;
-
- case FruitVisualRepresentation.Pineapple:
- return CatchSkinComponents.FruitApple;
-
- case FruitVisualRepresentation.Raspberry:
- return CatchSkinComponents.FruitOrange;
-
- case FruitVisualRepresentation.Banana:
- return CatchSkinComponents.FruitBananas;
-
- default:
- throw new ArgumentOutOfRangeException(nameof(hitObjectVisualRepresentation), hitObjectVisualRepresentation, null);
- }
+ ScalingContainer.RotateTo((RandomSingle(1) - 0.5f) * 40);
}
}
@@ -88,6 +51,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
Grape,
Pineapple,
Raspberry,
- Banana // banananananannaanana
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs
index 0877b5e248..7df06bd92d 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs
@@ -7,32 +7,36 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
- public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject
+ [Cached(typeof(IHasCatchObjectState))]
+ public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject, IHasCatchObjectState
{
public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject;
- public readonly Bindable HyperDash = new Bindable();
+ Bindable IHasCatchObjectState.AccentColour => AccentColour;
- public readonly Bindable ScaleBindable = new Bindable(1);
+ public Bindable HyperDash { get; } = new Bindable();
- public readonly Bindable IndexInBeatmap = new Bindable();
+ public Bindable ScaleBindable { get; } = new Bindable(1);
+
+ public Bindable IndexInBeatmap { get; } = new Bindable();
///
- /// The multiplicative factor applied to scale relative to scale.
+ /// The multiplicative factor applied to relative to scale.
///
protected virtual float ScaleFactor => 1;
///
- /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher.
+ /// The container internal transforms (such as scaling based on the circle size) are applied to.
///
- public virtual bool StaysOnPlate => true;
+ protected readonly Container ScalingContainer;
- public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor;
+ public Vector2 DisplaySize => ScalingContainer.Size * ScalingContainer.Scale;
- protected readonly Container ScaleContainer;
+ public float DisplayRotation => ScalingContainer.Rotation;
protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h)
: base(h)
@@ -40,11 +44,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
Origin = Anchor.Centre;
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
- AddInternal(ScaleContainer = new Container
+ AddInternal(ScalingContainer = new Container
{
- RelativeSizeAxes = Axes.Both,
- Origin = Anchor.Centre,
Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2)
});
}
@@ -53,12 +57,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
XBindable.BindValueChanged(x =>
{
- if (!IsOnPlate) X = x.NewValue;
+ X = x.NewValue;
}, true);
ScaleBindable.BindValueChanged(scale =>
{
- ScaleContainer.Scale = new Vector2(scale.NewValue * ScaleFactor);
+ ScalingContainer.Scale = new Vector2(scale.NewValue * ScaleFactor);
+ Size = DisplaySize;
}, true);
IndexInBeatmap.BindValueChanged(_ => UpdateComboColour());
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs
new file mode 100644
index 0000000000..81b61f0959
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs
@@ -0,0 +1,25 @@
+// 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 osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ ///
+ /// Provides a visual state of a .
+ ///
+ public interface IHasCatchObjectState
+ {
+ PalpableCatchHitObject HitObject { get; }
+
+ Bindable AccentColour { get; }
+
+ Bindable HyperDash { get; }
+
+ Vector2 DisplaySize { get; }
+
+ float DisplayRotation { get; }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs
new file mode 100644
index 0000000000..2d4de543c3
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs
@@ -0,0 +1,15 @@
+// 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;
+
+namespace osu.Game.Rulesets.Catch.Objects.Drawables
+{
+ ///
+ /// Provides a visual state of a .
+ ///
+ public interface IHasFruitState : IHasCatchObjectState
+ {
+ Bindable VisualRepresentation { get; }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BananaPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BananaPiece.cs
deleted file mode 100644
index fa8837dec5..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BananaPiece.cs
+++ /dev/null
@@ -1,30 +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 osu.Framework.Graphics;
-using osuTK;
-
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
-{
- public class BananaPiece : PulpFormation
- {
- public BananaPiece()
- {
- InternalChildren = new Drawable[]
- {
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(SMALL_PULP),
- Y = -0.3f
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_4 * 0.8f, LARGE_PULP_4 * 2.5f),
- Y = 0.05f,
- },
- };
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs
deleted file mode 100644
index c90407ae15..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs
+++ /dev/null
@@ -1,37 +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 osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Objects.Drawables;
-using osuTK;
-
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
-{
- public class DropletPiece : CompositeDrawable
- {
- public readonly Bindable HyperDash = new Bindable();
-
- public DropletPiece()
- {
- Size = new Vector2(CatchHitObject.OBJECT_RADIUS / 2);
- }
-
- [BackgroundDependencyLoader]
- private void load(DrawableHitObject drawableObject)
- {
- InternalChild = new Pulp
- {
- RelativeSizeAxes = Axes.Both,
- AccentColour = { BindTarget = drawableObject.AccentColour }
- };
-
- if (HyperDash.Value)
- {
- AddInternal(new HyperDropletBorderPiece());
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.cs
deleted file mode 100644
index 31487ee407..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.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;
-using JetBrains.Annotations;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Objects.Drawables;
-
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
-{
- internal class FruitPiece : CompositeDrawable
- {
- ///
- /// Because we're adding a border around the fruit, we need to scale down some.
- ///
- public const float RADIUS_ADJUST = 1.1f;
-
- public readonly Bindable VisualRepresentation = new Bindable();
- public readonly Bindable HyperDash = new Bindable();
-
- [CanBeNull]
- private DrawableCatchHitObject drawableHitObject;
-
- [CanBeNull]
- private BorderPiece borderPiece;
-
- public FruitPiece()
- {
- RelativeSizeAxes = Axes.Both;
- }
-
- [BackgroundDependencyLoader(permitNulls: true)]
- private void load([CanBeNull] DrawableHitObject drawable)
- {
- drawableHitObject = (DrawableCatchHitObject)drawable;
-
- AddInternal(getFruitFor(VisualRepresentation.Value));
-
- // if it is not part of a DHO, the border is always invisible.
- if (drawableHitObject != null)
- AddInternal(borderPiece = new BorderPiece());
-
- if (HyperDash.Value)
- AddInternal(new HyperBorderPiece());
- }
-
- protected override void Update()
- {
- if (borderPiece != null && drawableHitObject?.HitObject != null)
- borderPiece.Alpha = (float)Math.Clamp((drawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1);
- }
-
- private Drawable getFruitFor(FruitVisualRepresentation representation)
- {
- switch (representation)
- {
- case FruitVisualRepresentation.Pear:
- return new PearPiece();
-
- case FruitVisualRepresentation.Grape:
- return new GrapePiece();
-
- case FruitVisualRepresentation.Pineapple:
- return new PineapplePiece();
-
- case FruitVisualRepresentation.Banana:
- return new BananaPiece();
-
- case FruitVisualRepresentation.Raspberry:
- return new RaspberryPiece();
- }
-
- return Empty();
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/GrapePiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/GrapePiece.cs
deleted file mode 100644
index 15349c18d5..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/GrapePiece.cs
+++ /dev/null
@@ -1,42 +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 osu.Framework.Graphics;
-using osuTK;
-
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
-{
- public class GrapePiece : PulpFormation
- {
- public GrapePiece()
- {
- InternalChildren = new Drawable[]
- {
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(SMALL_PULP),
- Y = -0.25f,
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_3),
- Position = PositionAt(0, DISTANCE_FROM_CENTRE_3),
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_3),
- Position = PositionAt(120, DISTANCE_FROM_CENTRE_3),
- },
- new Pulp
- {
- Size = new Vector2(LARGE_PULP_3),
- AccentColour = { BindTarget = AccentColour },
- Position = PositionAt(240, DISTANCE_FROM_CENTRE_3),
- },
- };
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PearPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PearPiece.cs
deleted file mode 100644
index 3372a06996..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PearPiece.cs
+++ /dev/null
@@ -1,42 +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 osu.Framework.Graphics;
-using osuTK;
-
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
-{
- public class PearPiece : PulpFormation
- {
- public PearPiece()
- {
- InternalChildren = new Drawable[]
- {
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(SMALL_PULP),
- Y = -0.33f,
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_3),
- Position = PositionAt(60, DISTANCE_FROM_CENTRE_3),
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_3),
- Position = PositionAt(180, DISTANCE_FROM_CENTRE_3),
- },
- new Pulp
- {
- Size = new Vector2(LARGE_PULP_3),
- AccentColour = { BindTarget = AccentColour },
- Position = PositionAt(300, DISTANCE_FROM_CENTRE_3),
- },
- };
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PineapplePiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PineapplePiece.cs
deleted file mode 100644
index 7f80c58178..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PineapplePiece.cs
+++ /dev/null
@@ -1,48 +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 osu.Framework.Graphics;
-using osuTK;
-
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
-{
- public class PineapplePiece : PulpFormation
- {
- public PineapplePiece()
- {
- InternalChildren = new Drawable[]
- {
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(SMALL_PULP),
- Y = -0.3f,
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_4),
- Position = PositionAt(45, DISTANCE_FROM_CENTRE_4),
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_4),
- Position = PositionAt(135, DISTANCE_FROM_CENTRE_4),
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_4),
- Position = PositionAt(225, DISTANCE_FROM_CENTRE_4),
- },
- new Pulp
- {
- Size = new Vector2(LARGE_PULP_4),
- AccentColour = { BindTarget = AccentColour },
- Position = PositionAt(315, DISTANCE_FROM_CENTRE_4),
- },
- };
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/RaspberryPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/RaspberryPiece.cs
deleted file mode 100644
index 288ece95b2..0000000000
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/RaspberryPiece.cs
+++ /dev/null
@@ -1,48 +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 osu.Framework.Graphics;
-using osuTK;
-
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
-{
- public class RaspberryPiece : PulpFormation
- {
- public RaspberryPiece()
- {
- InternalChildren = new Drawable[]
- {
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(SMALL_PULP),
- Y = -0.34f,
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_4),
- Position = PositionAt(0, DISTANCE_FROM_CENTRE_4),
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_4),
- Position = PositionAt(90, DISTANCE_FROM_CENTRE_4),
- },
- new Pulp
- {
- AccentColour = { BindTarget = AccentColour },
- Size = new Vector2(LARGE_PULP_4),
- Position = PositionAt(180, DISTANCE_FROM_CENTRE_4),
- },
- new Pulp
- {
- Size = new Vector2(LARGE_PULP_4),
- AccentColour = { BindTarget = AccentColour },
- Position = PositionAt(270, DISTANCE_FROM_CENTRE_4),
- },
- };
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/BananaPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/BananaPiece.cs
new file mode 100644
index 0000000000..8da18a668a
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/BananaPiece.cs
@@ -0,0 +1,26 @@
+// 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;
+
+namespace osu.Game.Rulesets.Catch.Skinning.Default
+{
+ public class BananaPiece : CatchHitObjectPiece
+ {
+ protected override BorderPiece BorderPiece { get; }
+
+ public BananaPiece()
+ {
+ RelativeSizeAxes = Axes.Both;
+
+ InternalChildren = new Drawable[]
+ {
+ new BananaPulpFormation
+ {
+ AccentColour = { BindTarget = AccentColour },
+ },
+ BorderPiece = new BorderPiece(),
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/BananaPulpFormation.cs b/osu.Game.Rulesets.Catch/Skinning/Default/BananaPulpFormation.cs
new file mode 100644
index 0000000000..ee1cc68f7d
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/BananaPulpFormation.cs
@@ -0,0 +1,16 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Skinning.Default
+{
+ public class BananaPulpFormation : PulpFormation
+ {
+ public BananaPulpFormation()
+ {
+ AddPulp(new Vector2(0, -0.3f), new Vector2(SMALL_PULP));
+ AddPulp(new Vector2(0, 0.05f), new Vector2(LARGE_PULP_4 * 0.8f, LARGE_PULP_4 * 2.5f));
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BorderPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/BorderPiece.cs
similarity index 90%
rename from osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BorderPiece.cs
rename to osu.Game.Rulesets.Catch/Skinning/Default/BorderPiece.cs
index 1e7a0b0685..7308d6b499 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BorderPiece.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/BorderPiece.cs
@@ -3,10 +3,11 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Catch.Objects;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public class BorderPiece : Circle
{
diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs
new file mode 100644
index 0000000000..51c06c8e37
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs
@@ -0,0 +1,54 @@
+// 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 JetBrains.Annotations;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Catch.Skinning.Default
+{
+ public abstract class CatchHitObjectPiece : CompositeDrawable
+ {
+ public readonly Bindable AccentColour = new Bindable();
+ public readonly Bindable HyperDash = new Bindable();
+
+ [Resolved]
+ protected IHasCatchObjectState ObjectState { get; private set; }
+
+ ///
+ /// A part of this piece that will be faded out while falling in the playfield.
+ ///
+ [CanBeNull]
+ protected virtual BorderPiece BorderPiece => null;
+
+ ///
+ /// A part of this piece that will be only visible when is true.
+ ///
+ [CanBeNull]
+ protected virtual HyperBorderPiece HyperBorderPiece => null;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ AccentColour.BindTo(ObjectState.AccentColour);
+ HyperDash.BindTo(ObjectState.HyperDash);
+
+ HyperDash.BindValueChanged(hyper =>
+ {
+ if (HyperBorderPiece != null)
+ HyperBorderPiece.Alpha = hyper.NewValue ? 1 : 0;
+ }, true);
+ }
+
+ protected override void Update()
+ {
+ if (BorderPiece != null)
+ BorderPiece.Alpha = (float)Math.Clamp((ObjectState.HitObject.StartTime - Time.Current) / 500, 0, 1);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs
new file mode 100644
index 0000000000..8b1052dfe2
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.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.Game.Rulesets.Catch.Objects;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Skinning.Default
+{
+ public class DropletPiece : CatchHitObjectPiece
+ {
+ protected override HyperBorderPiece HyperBorderPiece { get; }
+
+ public DropletPiece()
+ {
+ Size = new Vector2(CatchHitObject.OBJECT_RADIUS / 2);
+
+ InternalChildren = new Drawable[]
+ {
+ new Pulp
+ {
+ RelativeSizeAxes = Axes.Both,
+ AccentColour = { BindTarget = AccentColour }
+ },
+ HyperBorderPiece = new HyperDropletBorderPiece()
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs
new file mode 100644
index 0000000000..49f128c960
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs
@@ -0,0 +1,46 @@
+// 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.Graphics;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Catch.Skinning.Default
+{
+ internal class FruitPiece : CatchHitObjectPiece
+ {
+ ///
+ /// Because we're adding a border around the fruit, we need to scale down some.
+ ///
+ public const float RADIUS_ADJUST = 1.1f;
+
+ public readonly Bindable VisualRepresentation = new Bindable();
+
+ protected override BorderPiece BorderPiece { get; }
+ protected override HyperBorderPiece HyperBorderPiece { get; }
+
+ public FruitPiece()
+ {
+ RelativeSizeAxes = Axes.Both;
+
+ InternalChildren = new Drawable[]
+ {
+ new FruitPulpFormation
+ {
+ AccentColour = { BindTarget = AccentColour },
+ VisualRepresentation = { BindTarget = VisualRepresentation }
+ },
+ BorderPiece = new BorderPiece(),
+ HyperBorderPiece = new HyperBorderPiece()
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ var fruitState = (IHasFruitState)ObjectState;
+ VisualRepresentation.BindTo(fruitState.VisualRepresentation);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPulpFormation.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPulpFormation.cs
new file mode 100644
index 0000000000..88e0b5133a
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPulpFormation.cs
@@ -0,0 +1,59 @@
+// 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.Game.Rulesets.Catch.Objects.Drawables;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Skinning.Default
+{
+ public class FruitPulpFormation : PulpFormation
+ {
+ public readonly Bindable VisualRepresentation = new Bindable();
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ VisualRepresentation.BindValueChanged(setFormation, true);
+ }
+
+ private void setFormation(ValueChangedEvent visualRepresentation)
+ {
+ Clear();
+
+ switch (visualRepresentation.NewValue)
+ {
+ case FruitVisualRepresentation.Pear:
+ AddPulp(new Vector2(0, -0.33f), new Vector2(SMALL_PULP));
+ AddPulp(PositionAt(60, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
+ AddPulp(PositionAt(180, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
+ AddPulp(PositionAt(300, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
+ break;
+
+ case FruitVisualRepresentation.Grape:
+ AddPulp(new Vector2(0, -0.25f), new Vector2(SMALL_PULP));
+ AddPulp(PositionAt(0, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
+ AddPulp(PositionAt(120, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
+ AddPulp(PositionAt(240, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
+ break;
+
+ case FruitVisualRepresentation.Pineapple:
+ AddPulp(new Vector2(0, -0.3f), new Vector2(SMALL_PULP));
+ AddPulp(PositionAt(45, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
+ AddPulp(PositionAt(135, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
+ AddPulp(PositionAt(225, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
+ AddPulp(PositionAt(315, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
+ break;
+
+ case FruitVisualRepresentation.Raspberry:
+ AddPulp(new Vector2(0, -0.34f), new Vector2(SMALL_PULP));
+ AddPulp(PositionAt(0, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
+ AddPulp(PositionAt(90, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
+ AddPulp(PositionAt(180, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
+ AddPulp(PositionAt(270, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
+ break;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperBorderPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/HyperBorderPiece.cs
similarity index 91%
rename from osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperBorderPiece.cs
rename to osu.Game.Rulesets.Catch/Skinning/Default/HyperBorderPiece.cs
index 60bb07e89d..d160956a6e 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperBorderPiece.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/HyperBorderPiece.cs
@@ -4,7 +4,7 @@
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.UI;
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public class HyperBorderPiece : BorderPiece
{
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperDropletBorderPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/HyperDropletBorderPiece.cs
similarity index 85%
rename from osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperDropletBorderPiece.cs
rename to osu.Game.Rulesets.Catch/Skinning/Default/HyperDropletBorderPiece.cs
index 1bd9fd6bb2..53a487b97f 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperDropletBorderPiece.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/HyperDropletBorderPiece.cs
@@ -1,7 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public class HyperDropletBorderPiece : HyperBorderPiece
{
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Skinning/Default/Pulp.cs
similarity index 95%
rename from osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/Pulp.cs
rename to osu.Game.Rulesets.Catch/Skinning/Default/Pulp.cs
index d3e4945611..96c6233b41 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/Pulp.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/Pulp.cs
@@ -8,10 +8,12 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public class Pulp : Circle
{
+ public readonly Bindable AccentColour = new Bindable();
+
public Pulp()
{
RelativePositionAxes = Axes.Both;
@@ -22,8 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
Colour = Color4.White.Opacity(0.9f);
}
- public readonly Bindable AccentColour = new Bindable();
-
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PulpFormation.cs b/osu.Game.Rulesets.Catch/Skinning/Default/PulpFormation.cs
similarity index 59%
rename from osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PulpFormation.cs
rename to osu.Game.Rulesets.Catch/Skinning/Default/PulpFormation.cs
index 1df548e70a..8753aa4077 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PulpFormation.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/Default/PulpFormation.cs
@@ -2,19 +2,17 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Objects.Drawables;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public abstract class PulpFormation : CompositeDrawable
{
- protected readonly IBindable AccentColour = new Bindable();
+ public readonly Bindable AccentColour = new Bindable();
protected const float LARGE_PULP_3 = 16f * FruitPiece.RADIUS_ADJUST;
protected const float DISTANCE_FROM_CENTRE_3 = 0.15f;
@@ -24,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
protected const float SMALL_PULP = LARGE_PULP_3 / 2;
+ private int pulpsInUse;
+
protected PulpFormation()
{
RelativeSizeAxes = Axes.Both;
@@ -33,11 +33,24 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
distance * MathF.Sin(angle * MathF.PI / 180),
distance * MathF.Cos(angle * MathF.PI / 180));
- [BackgroundDependencyLoader]
- private void load(DrawableHitObject drawableObject)
+ protected void Clear()
{
- DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject;
- AccentColour.BindTo(drawableCatchObject.AccentColour);
+ for (int i = 0; i < pulpsInUse; i++)
+ InternalChildren[i].Alpha = 0;
+ pulpsInUse = 0;
+ }
+
+ protected void AddPulp(Vector2 position, Vector2 size)
+ {
+ if (pulpsInUse == InternalChildren.Count)
+ AddInternal(new Pulp { AccentColour = { BindTarget = AccentColour } });
+
+ var pulp = InternalChildren[pulpsInUse];
+ pulp.Position = position;
+ pulp.Size = size;
+ pulp.Alpha = 1;
+
+ pulpsInUse++;
}
}
}
diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs
similarity index 83%
rename from osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs
rename to osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs
index 22db147e32..41fd0fe776 100644
--- a/osu.Game.Rulesets.Catch/Skinning/CatchLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs
@@ -1,15 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using Humanizer;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Skinning;
-using osuTK;
using osuTK.Graphics;
using static osu.Game.Skinning.LegacySkinConfiguration;
-namespace osu.Game.Rulesets.Catch.Skinning
+namespace osu.Game.Rulesets.Catch.Skinning.Legacy
{
public class CatchLegacySkinTransformer : LegacySkinTransformer
{
@@ -40,20 +38,21 @@ namespace osu.Game.Rulesets.Catch.Skinning
switch (catchSkinComponent.Component)
{
- case CatchSkinComponents.FruitApple:
- case CatchSkinComponents.FruitBananas:
- case CatchSkinComponents.FruitOrange:
- case CatchSkinComponents.FruitGrapes:
- case CatchSkinComponents.FruitPear:
- var lookupName = catchSkinComponent.Component.ToString().Kebaberize();
- if (GetTexture(lookupName) != null)
- return new LegacyFruitPiece(lookupName);
+ case CatchSkinComponents.Fruit:
+ if (GetTexture("fruit-pear") != null)
+ return new LegacyFruitPiece();
+
+ break;
+
+ case CatchSkinComponents.Banana:
+ if (GetTexture("fruit-bananas") != null)
+ return new LegacyBananaPiece();
break;
case CatchSkinComponents.Droplet:
if (GetTexture("fruit-drop") != null)
- return new LegacyFruitPiece("fruit-drop") { Scale = new Vector2(0.8f) };
+ return new LegacyDropletPiece();
break;
diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchComboCounter.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs
similarity index 98%
rename from osu.Game.Rulesets.Catch/Skinning/LegacyCatchComboCounter.cs
rename to osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs
index 34608b07ff..f797ae75c2 100644
--- a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchComboCounter.cs
+++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs
@@ -9,7 +9,7 @@ using osuTK;
using osuTK.Graphics;
using static osu.Game.Skinning.LegacySkinConfiguration;
-namespace osu.Game.Rulesets.Catch.Skinning
+namespace osu.Game.Rulesets.Catch.Skinning.Legacy
{
///
/// A combo counter implementation that visually behaves almost similar to stable's osu!catch combo counter.
diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs
new file mode 100644
index 0000000000..969cc38e5b
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs
@@ -0,0 +1,45 @@
+// 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.Game.Rulesets.Catch.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Catch.Skinning.Legacy
+{
+ internal class LegacyFruitPiece : LegacyCatchHitObjectPiece
+ {
+ public readonly Bindable VisualRepresentation = new Bindable();
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ var fruitState = (IHasFruitState)ObjectState;
+ VisualRepresentation.BindTo(fruitState.VisualRepresentation);
+
+ VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true);
+ }
+
+ private void setTexture(FruitVisualRepresentation visualRepresentation)
+ {
+ switch (visualRepresentation)
+ {
+ case FruitVisualRepresentation.Pear:
+ SetTexture(Skin.GetTexture("fruit-pear"), Skin.GetTexture("fruit-pear-overlay"));
+ break;
+
+ case FruitVisualRepresentation.Grape:
+ SetTexture(Skin.GetTexture("fruit-grapes"), Skin.GetTexture("fruit-grapes-overlay"));
+ break;
+
+ case FruitVisualRepresentation.Pineapple:
+ SetTexture(Skin.GetTexture("fruit-apple"), Skin.GetTexture("fruit-apple-overlay"));
+ break;
+
+ case FruitVisualRepresentation.Raspberry:
+ SetTexture(Skin.GetTexture("fruit-orange"), Skin.GetTexture("fruit-orange-overlay"));
+ break;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyBananaPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyBananaPiece.cs
new file mode 100644
index 0000000000..f80e50c8c0
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/LegacyBananaPiece.cs
@@ -0,0 +1,20 @@
+// 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.Textures;
+
+namespace osu.Game.Rulesets.Catch.Skinning
+{
+ public class LegacyBananaPiece : LegacyCatchHitObjectPiece
+ {
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Texture texture = Skin.GetTexture("fruit-bananas");
+ Texture overlayTexture = Skin.GetTexture("fruit-bananas-overlay");
+
+ SetTexture(texture, overlayTexture);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs
new file mode 100644
index 0000000000..4b1f5a4724
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs
@@ -0,0 +1,90 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Pooling;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Rulesets.Catch.Objects.Drawables;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Skinning;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Catch.Skinning
+{
+ public abstract class LegacyCatchHitObjectPiece : PoolableDrawable
+ {
+ public readonly Bindable AccentColour = new Bindable();
+ public readonly Bindable HyperDash = new Bindable();
+
+ private readonly Sprite colouredSprite;
+ private readonly Sprite overlaySprite;
+ private readonly Sprite hyperSprite;
+
+ [Resolved]
+ protected ISkinSource Skin { get; private set; }
+
+ [Resolved]
+ protected IHasCatchObjectState ObjectState { get; private set; }
+
+ protected LegacyCatchHitObjectPiece()
+ {
+ RelativeSizeAxes = Axes.Both;
+
+ InternalChildren = new Drawable[]
+ {
+ colouredSprite = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ overlaySprite = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ hyperSprite = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Blending = BlendingParameters.Additive,
+ Depth = 1,
+ Alpha = 0,
+ Scale = new Vector2(1.2f),
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ AccentColour.BindTo(ObjectState.AccentColour);
+ HyperDash.BindTo(ObjectState.HyperDash);
+
+ hyperSprite.Colour = Skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ??
+ Skin.GetConfig(CatchSkinColour.HyperDash)?.Value ??
+ Catcher.DEFAULT_HYPER_DASH_COLOUR;
+
+ AccentColour.BindValueChanged(colour =>
+ {
+ colouredSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue);
+ }, true);
+
+ HyperDash.BindValueChanged(hyper =>
+ {
+ hyperSprite.Alpha = hyper.NewValue ? 0.7f : 0;
+ }, true);
+ }
+
+ protected void SetTexture(Texture texture, Texture overlayTexture)
+ {
+ colouredSprite.Texture = texture;
+ overlaySprite.Texture = overlayTexture;
+ hyperSprite.Texture = texture;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyDropletPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyDropletPiece.cs
new file mode 100644
index 0000000000..8f4331d2a3
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Skinning/LegacyDropletPiece.cs
@@ -0,0 +1,26 @@
+// 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.Textures;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Skinning
+{
+ public class LegacyDropletPiece : LegacyCatchHitObjectPiece
+ {
+ public LegacyDropletPiece()
+ {
+ Scale = new Vector2(0.8f);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Texture texture = Skin.GetTexture("fruit-drop");
+ Texture overlayTexture = Skin.GetTexture("fruit-drop-overlay");
+
+ SetTexture(texture, overlayTexture);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs
deleted file mode 100644
index b8648f46f0..0000000000
--- a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs
+++ /dev/null
@@ -1,83 +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 osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
-using osu.Game.Rulesets.Catch.Objects.Drawables;
-using osu.Game.Rulesets.Catch.UI;
-using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Skinning;
-using osuTK;
-using osuTK.Graphics;
-
-namespace osu.Game.Rulesets.Catch.Skinning
-{
- internal class LegacyFruitPiece : CompositeDrawable
- {
- private readonly string lookupName;
-
- private readonly Bindable accentColour = new Bindable();
- private readonly Bindable hyperDash = new Bindable();
- private Sprite colouredSprite;
-
- public LegacyFruitPiece(string lookupName)
- {
- this.lookupName = lookupName;
- RelativeSizeAxes = Axes.Both;
- }
-
- [BackgroundDependencyLoader]
- private void load(DrawableHitObject drawableObject, ISkinSource skin)
- {
- var drawableCatchObject = (DrawablePalpableCatchHitObject)drawableObject;
-
- accentColour.BindTo(drawableCatchObject.AccentColour);
- hyperDash.BindTo(drawableCatchObject.HyperDash);
-
- InternalChildren = new Drawable[]
- {
- colouredSprite = new Sprite
- {
- Texture = skin.GetTexture(lookupName),
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- },
- new Sprite
- {
- Texture = skin.GetTexture($"{lookupName}-overlay"),
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- },
- };
-
- if (hyperDash.Value)
- {
- var hyperDashOverlay = new Sprite
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Blending = BlendingParameters.Additive,
- Depth = 1,
- Alpha = 0.7f,
- Scale = new Vector2(1.2f),
- Texture = skin.GetTexture(lookupName),
- Colour = skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ??
- skin.GetConfig(CatchSkinColour.HyperDash)?.Value ??
- Catcher.DEFAULT_HYPER_DASH_COLOUR,
- };
-
- AddInternal(hyperDashOverlay);
- }
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- accentColour.BindValueChanged(colour => colouredSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index df87359ed6..73420a9eda 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI
public CatchPlayfield(BeatmapDifficulty difficulty, Func> createDrawableRepresentation)
{
- var droppedObjectContainer = new Container
+ var droppedObjectContainer = new Container
{
RelativeSizeAxes = Axes.Both,
};
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Catch.UI
((DrawableCatchHitObject)d).CheckPosition = checkIfWeCanCatch;
}
- private bool checkIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj);
+ private bool checkIfWeCanCatch(CatchHitObject obj) => CatcherArea.MovableCatcher.CanCatch(obj);
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
=> CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result);
diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs
index 2a3447c80a..f164c2655a 100644
--- a/osu.Game.Rulesets.Catch/UI/Catcher.cs
+++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs
@@ -14,9 +14,11 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
+using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Skinning;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
@@ -51,9 +53,15 @@ namespace osu.Game.Rulesets.Catch.UI
private CatcherTrailDisplay trails;
- private readonly Container droppedObjectTarget;
+ ///
+ /// Contains caught objects on the plate.
+ ///
+ private readonly Container caughtObjectContainer;
- private readonly Container caughtFruitContainer;
+ ///
+ /// Contains objects dropped from the plate.
+ ///
+ private readonly Container droppedObjectTarget;
public CatcherAnimationState CurrentState { get; private set; }
@@ -106,7 +114,11 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly DrawablePool hitExplosionPool;
private readonly Container hitExplosionContainer;
- public Catcher([NotNull] Container trailsTarget, [NotNull] Container droppedObjectTarget, BeatmapDifficulty difficulty = null)
+ private readonly DrawablePool caughtFruitPool;
+ private readonly DrawablePool caughtBananaPool;
+ private readonly DrawablePool caughtDropletPool;
+
+ public Catcher([NotNull] Container trailsTarget, [NotNull] Container droppedObjectTarget, BeatmapDifficulty difficulty = null)
{
this.trailsTarget = trailsTarget;
this.droppedObjectTarget = droppedObjectTarget;
@@ -122,7 +134,11 @@ namespace osu.Game.Rulesets.Catch.UI
InternalChildren = new Drawable[]
{
hitExplosionPool = new DrawablePool(10),
- caughtFruitContainer = new Container
+ caughtFruitPool = new DrawablePool(50),
+ caughtBananaPool = new DrawablePool(100),
+ // less capacity is needed compared to fruit because droplet is not stacked
+ caughtDropletPool = new DrawablePool(25),
+ caughtObjectContainer = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
@@ -170,7 +186,7 @@ namespace osu.Game.Rulesets.Catch.UI
///
/// Creates proxied content to be displayed beneath hitobjects.
///
- public Drawable CreateProxiedContent() => caughtFruitContainer.CreateProxy();
+ public Drawable CreateProxiedContent() => caughtObjectContainer.CreateProxy();
///
/// Calculates the scale of the catcher based off the provided beatmap difficulty.
@@ -190,11 +206,9 @@ namespace osu.Game.Rulesets.Catch.UI
internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(calculateScale(difficulty));
///
- /// Let the catcher attempt to catch a fruit.
+ /// Determine if this catcher can catch a in the current position.
///
- /// The fruit to catch.
- /// Whether the catch is possible.
- public bool AttemptCatch(CatchHitObject hitObject)
+ public bool CanCatch(CatchHitObject hitObject)
{
if (!(hitObject is PalpableCatchHitObject fruit))
return false;
@@ -205,21 +219,38 @@ namespace osu.Game.Rulesets.Catch.UI
var catchObjectPosition = fruit.X;
var catcherPosition = Position.X;
- var validCatch =
- catchObjectPosition >= catcherPosition - halfCatchWidth &&
- catchObjectPosition <= catcherPosition + halfCatchWidth;
+ return catchObjectPosition >= catcherPosition - halfCatchWidth &&
+ catchObjectPosition <= catcherPosition + halfCatchWidth;
+ }
- if (validCatch)
- placeCaughtObject(fruit);
+ public void OnNewResult(DrawableCatchHitObject drawableObject, JudgementResult result)
+ {
+ var catchResult = (CatchJudgementResult)result;
+ catchResult.CatcherAnimationState = CurrentState;
+ catchResult.CatcherHyperDash = HyperDashing;
+
+ if (!(drawableObject is DrawablePalpableCatchHitObject palpableObject)) return;
+
+ var hitObject = palpableObject.HitObject;
+
+ if (result.IsHit)
+ {
+ var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplaySize.X / 2);
+
+ placeCaughtObject(palpableObject, positionInStack);
+
+ if (hitLighting.Value)
+ addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value);
+ }
// droplet doesn't affect the catcher state
- if (fruit is TinyDroplet) return validCatch;
+ if (hitObject is TinyDroplet) return;
- if (validCatch && fruit.HyperDash)
+ if (result.IsHit && hitObject.HyperDash)
{
- var target = fruit.HyperDashTarget;
- var timeDifference = target.StartTime - fruit.StartTime;
- double positionDifference = target.X - catcherPosition;
+ var target = hitObject.HyperDashTarget;
+ var timeDifference = target.StartTime - hitObject.StartTime;
+ double positionDifference = target.X - X;
var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
SetHyperDashState(Math.Abs(velocity), target.X);
@@ -227,12 +258,30 @@ namespace osu.Game.Rulesets.Catch.UI
else
SetHyperDashState();
- if (validCatch)
- updateState(fruit.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle);
- else if (!(fruit is Banana))
+ if (result.IsHit)
+ updateState(hitObject.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle);
+ else if (!(hitObject is Banana))
updateState(CatcherAnimationState.Fail);
+ }
- return validCatch;
+ public void OnRevertResult(DrawableCatchHitObject drawableObject, JudgementResult result)
+ {
+ var catchResult = (CatchJudgementResult)result;
+
+ if (CurrentState != catchResult.CatcherAnimationState)
+ updateState(catchResult.CatcherAnimationState);
+
+ if (HyperDashing != catchResult.CatcherHyperDash)
+ {
+ if (catchResult.CatcherHyperDash)
+ SetHyperDashState(2);
+ else
+ SetHyperDashState();
+ }
+
+ caughtObjectContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject);
+ droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject);
+ hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject);
}
///
@@ -415,137 +464,121 @@ namespace osu.Game.Rulesets.Catch.UI
updateCatcher();
}
- private void placeCaughtObject(PalpableCatchHitObject source)
+ private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position)
{
- var caughtObject = createCaughtObject(source);
+ var caughtObject = getCaughtObject(drawableObject.HitObject);
if (caughtObject == null) return;
- caughtObject.RelativePositionAxes = Axes.None;
- caughtObject.X = source.X - X;
- caughtObject.IsOnPlate = true;
-
+ caughtObject.CopyStateFrom(drawableObject);
caughtObject.Anchor = Anchor.TopCentre;
- caughtObject.Origin = Anchor.Centre;
- caughtObject.Scale *= 0.5f;
- caughtObject.LifetimeStart = source.StartTime;
- caughtObject.LifetimeEnd = double.MaxValue;
+ caughtObject.Position = position;
+ caughtObject.Scale /= 2;
- adjustPositionInStack(caughtObject);
-
- caughtFruitContainer.Add(caughtObject);
-
- addLighting(caughtObject);
+ caughtObjectContainer.Add(caughtObject);
if (!caughtObject.StaysOnPlate)
removeFromPlate(caughtObject, DroppedObjectAnimation.Explode);
}
- private void adjustPositionInStack(DrawablePalpableCatchHitObject caughtObject)
+ private Vector2 computePositionInStack(Vector2 position, float displayRadius)
{
const float radius_div_2 = CatchHitObject.OBJECT_RADIUS / 2;
const float allowance = 10;
- float caughtObjectRadius = caughtObject.DisplayRadius;
-
- while (caughtFruitContainer.Any(f => Vector2Extensions.Distance(f.Position, caughtObject.Position) < (caughtObjectRadius + radius_div_2) / (allowance / 2)))
+ while (caughtObjectContainer.Any(f => Vector2Extensions.Distance(f.Position, position) < (displayRadius + radius_div_2) / (allowance / 2)))
{
- float diff = (caughtObjectRadius + radius_div_2) / allowance;
+ float diff = (displayRadius + radius_div_2) / allowance;
- caughtObject.X += (RNG.NextSingle() - 0.5f) * diff * 2;
- caughtObject.Y -= RNG.NextSingle() * diff;
+ position.X += (RNG.NextSingle() - 0.5f) * diff * 2;
+ position.Y -= RNG.NextSingle() * diff;
}
- caughtObject.X = Math.Clamp(caughtObject.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2);
+ position.X = Math.Clamp(position.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2);
+
+ return position;
}
- private void addLighting(DrawablePalpableCatchHitObject caughtObject)
+ private void addLighting(CatchHitObject hitObject, float x, Color4 colour)
{
- if (!hitLighting.Value) return;
-
HitExplosion hitExplosion = hitExplosionPool.Get();
- hitExplosion.X = caughtObject.X;
- hitExplosion.Scale = new Vector2(caughtObject.HitObject.Scale);
- hitExplosion.ObjectColour = caughtObject.AccentColour.Value;
+ hitExplosion.HitObject = hitObject;
+ hitExplosion.X = x;
+ hitExplosion.Scale = new Vector2(hitObject.Scale);
+ hitExplosion.ObjectColour = colour;
hitExplosionContainer.Add(hitExplosion);
}
- private DrawablePalpableCatchHitObject createCaughtObject(PalpableCatchHitObject source)
+ private CaughtObject getCaughtObject(PalpableCatchHitObject source)
{
switch (source)
{
- case Banana banana:
- return new DrawableBanana(banana);
+ case Fruit _:
+ return caughtFruitPool.Get();
- case Fruit fruit:
- return new DrawableFruit(fruit);
+ case Banana _:
+ return caughtBananaPool.Get();
- case TinyDroplet tiny:
- return new DrawableTinyDroplet(tiny);
-
- case Droplet droplet:
- return new DrawableDroplet(droplet);
+ case Droplet _:
+ return caughtDropletPool.Get();
default:
return null;
}
}
+ private CaughtObject getDroppedObject(CaughtObject caughtObject)
+ {
+ var droppedObject = getCaughtObject(caughtObject.HitObject);
+
+ droppedObject.CopyStateFrom(caughtObject);
+ droppedObject.Anchor = Anchor.TopLeft;
+ droppedObject.Position = caughtObjectContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget);
+
+ return droppedObject;
+ }
+
private void clearPlate(DroppedObjectAnimation animation)
{
- var caughtObjects = caughtFruitContainer.Children.ToArray();
- caughtFruitContainer.Clear(false);
+ var droppedObjects = caughtObjectContainer.Children.Select(getDroppedObject).ToArray();
- droppedObjectTarget.AddRange(caughtObjects);
+ caughtObjectContainer.Clear(false);
- foreach (var caughtObject in caughtObjects)
- drop(caughtObject, animation);
+ droppedObjectTarget.AddRange(droppedObjects);
+
+ foreach (var droppedObject in droppedObjects)
+ applyDropAnimation(droppedObject, animation);
}
- private void removeFromPlate(DrawablePalpableCatchHitObject caughtObject, DroppedObjectAnimation animation)
+ private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation)
{
- if (!caughtFruitContainer.Remove(caughtObject))
- throw new InvalidOperationException("Can only drop a caught object on the plate");
+ var droppedObject = getDroppedObject(caughtObject);
- droppedObjectTarget.Add(caughtObject);
+ caughtObjectContainer.Remove(caughtObject);
- drop(caughtObject, animation);
+ droppedObjectTarget.Add(droppedObject);
+
+ applyDropAnimation(droppedObject, animation);
}
- private void drop(DrawablePalpableCatchHitObject d, DroppedObjectAnimation animation)
+ private void applyDropAnimation(Drawable d, DroppedObjectAnimation animation)
{
- var originalX = d.X * Scale.X;
- var startTime = Clock.CurrentTime;
-
- d.Anchor = Anchor.TopLeft;
- d.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(d.DrawPosition, droppedObjectTarget);
-
- // we cannot just apply the transforms because DHO clears transforms when state is updated
- d.ApplyCustomUpdateState += (o, state) => animate(o, animation, originalX, startTime);
- if (d.IsLoaded)
- animate(d, animation, originalX, startTime);
- }
-
- private void animate(Drawable d, DroppedObjectAnimation animation, float originalX, double startTime)
- {
- using (d.BeginAbsoluteSequence(startTime))
+ switch (animation)
{
- switch (animation)
- {
- case DroppedObjectAnimation.Drop:
- d.MoveToY(d.Y + 75, 750, Easing.InSine);
- d.FadeOut(750);
- break;
+ case DroppedObjectAnimation.Drop:
+ d.MoveToY(d.Y + 75, 750, Easing.InSine);
+ d.FadeOut(750);
+ break;
- case DroppedObjectAnimation.Explode:
- d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine);
- d.MoveToX(d.X + originalX * 6, 1000);
- d.FadeOut(750);
- break;
- }
-
- d.Expire();
+ case DroppedObjectAnimation.Explode:
+ var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtObjectContainer).X * Scale.X;
+ d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine);
+ d.MoveToX(d.X + originalX * 6, 1000);
+ d.FadeOut(750);
+ break;
}
+
+ d.Expire();
}
private enum DroppedObjectAnimation
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index 539776354c..44adbd5512 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -5,7 +5,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
-using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Judgements;
@@ -22,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.UI
public readonly Catcher MovableCatcher;
private readonly CatchComboDisplay comboDisplay;
- public CatcherArea(Container droppedObjectContainer, BeatmapDifficulty difficulty = null)
+ public CatcherArea(Container droppedObjectContainer, BeatmapDifficulty difficulty = null)
{
Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE);
Children = new Drawable[]
@@ -42,6 +41,8 @@ namespace osu.Game.Rulesets.Catch.UI
public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result)
{
+ MovableCatcher.OnNewResult(hitObject, result);
+
if (!result.Type.IsScorable())
return;
@@ -56,12 +57,10 @@ namespace osu.Game.Rulesets.Catch.UI
comboDisplay.OnNewResult(hitObject, result);
}
- public void OnRevertResult(DrawableCatchHitObject fruit, JudgementResult result)
- => comboDisplay.OnRevertResult(fruit, result);
-
- public bool AttemptCatch(CatchHitObject obj)
+ public void OnRevertResult(DrawableCatchHitObject hitObject, JudgementResult result)
{
- return MovableCatcher.AttemptCatch(obj);
+ comboDisplay.OnRevertResult(hitObject, result);
+ MovableCatcher.OnRevertResult(hitObject, result);
}
protected override void UpdateAfterChildren()
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
index f7e9fd19a7..fa65190032 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
@@ -6,6 +6,7 @@ using JetBrains.Annotations;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Pooling;
using osu.Framework.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;
@@ -20,6 +21,8 @@ namespace osu.Game.Rulesets.Catch.UI
{
private readonly Catcher catcher;
+ private readonly DrawablePool trailPool;
+
private readonly Container dashTrails;
private readonly Container hyperDashTrails;
private readonly Container endGlowSprites;
@@ -80,8 +83,9 @@ namespace osu.Game.Rulesets.Catch.UI
RelativeSizeAxes = Axes.Both;
- InternalChildren = new[]
+ InternalChildren = new Drawable[]
{
+ trailPool = new DrawablePool(30),
dashTrails = new Container { RelativeSizeAxes = Axes.Both },
hyperDashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
endGlowSprites = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
@@ -118,14 +122,14 @@ namespace osu.Game.Rulesets.Catch.UI
{
var texture = (catcher.CurrentDrawableCatcher as TextureAnimation)?.CurrentFrame ?? ((Sprite)catcher.CurrentDrawableCatcher).Texture;
- var sprite = new CatcherTrailSprite(texture)
- {
- Anchor = catcher.Anchor,
- Scale = catcher.Scale,
- Blending = BlendingParameters.Additive,
- RelativePositionAxes = catcher.RelativePositionAxes,
- Position = catcher.Position
- };
+ CatcherTrailSprite sprite = trailPool.Get();
+
+ sprite.Texture = texture;
+ sprite.Anchor = catcher.Anchor;
+ sprite.Scale = catcher.Scale;
+ sprite.Blending = BlendingParameters.Additive;
+ sprite.RelativePositionAxes = catcher.RelativePositionAxes;
+ sprite.Position = catcher.Position;
target.Add(sprite);
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs
index 56cb7dbfda..0e3e409fac 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs
@@ -1,22 +1,40 @@
// 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.Pooling;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osuTK;
namespace osu.Game.Rulesets.Catch.UI
{
- public class CatcherTrailSprite : Sprite
+ public class CatcherTrailSprite : PoolableDrawable
{
- public CatcherTrailSprite(Texture texture)
+ public Texture Texture
{
- Texture = texture;
+ set => sprite.Texture = value;
+ }
+
+ private readonly Sprite sprite;
+
+ public CatcherTrailSprite()
+ {
+ InternalChild = sprite = new Sprite
+ {
+ RelativeSizeAxes = Axes.Both
+ };
Size = new Vector2(CatcherArea.CATCHER_SIZE);
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE;
}
+
+ protected override void FreeAfterUse()
+ {
+ ClearTransforms();
+ base.FreeAfterUse();
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs
index 24ca778248..26627422e1 100644
--- a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs
+++ b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Pooling;
using osu.Framework.Utils;
+using osu.Game.Rulesets.Catch.Objects;
using osuTK;
using osuTK.Graphics;
@@ -15,6 +16,7 @@ namespace osu.Game.Rulesets.Catch.UI
public class HitExplosion : PoolableDrawable
{
private Color4 objectColour;
+ public CatchHitObject HitObject;
public Color4 ObjectColour
{
diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaHitObjectComposer.cs
index c9551ee79e..aaf96c63a6 100644
--- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaHitObjectComposer.cs
@@ -15,7 +15,7 @@ using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit;
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs
index 0c56f7bcf4..4dc6700786 100644
--- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects;
using osuTK;
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditBodyPiece.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditBodyPiece.cs
index 5fa687298a..f5067ea366 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditBodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditBodyPiece.cs
@@ -4,7 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.Skinning.Default;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints.Components
{
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditNotePiece.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditNotePiece.cs
index 8773a39939..9c9273de3a 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditNotePiece.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditNotePiece.cs
@@ -4,7 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.Skinning.Default;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints.Components
{
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
index 01d572447b..324670c4b2 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
@@ -9,7 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Input;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 906f7382c5..59c766fd84 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -26,7 +26,7 @@ using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Scoring;
-using osu.Game.Rulesets.Mania.Skinning;
+using osu.Game.Rulesets.Mania.Skinning.Legacy;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Scoring;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index a64cc6dc67..4f062753a6 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -4,9 +4,9 @@
using System;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index b3402d13e4..b512986ccb 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -5,7 +5,7 @@ using System.Diagnostics;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Default/DefaultBodyPiece.cs
similarity index 98%
rename from osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs
rename to osu.Game.Rulesets.Mania/Skinning/Default/DefaultBodyPiece.cs
index 9999983af5..db1ac6da88 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Default/DefaultBodyPiece.cs
@@ -5,16 +5,17 @@ using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Layout;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
+using osuTK.Graphics;
-namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Mania.Skinning.Default
{
///
/// Represents length-wise portion of a hold note.
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Default/DefaultNotePiece.cs
similarity index 97%
rename from osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultNotePiece.cs
rename to osu.Game.Rulesets.Mania/Skinning/Default/DefaultNotePiece.cs
index 29f5217fd8..c9c3cff799 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultNotePiece.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Default/DefaultNotePiece.cs
@@ -4,7 +4,6 @@
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -12,8 +11,9 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
+using osuTK.Graphics;
-namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Mania.Skinning.Default
{
///
/// Represents the static hit markers of notes.
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/IHoldNoteBody.cs b/osu.Game.Rulesets.Mania/Skinning/Default/IHoldNoteBody.cs
similarity index 88%
rename from osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/IHoldNoteBody.cs
rename to osu.Game.Rulesets.Mania/Skinning/Default/IHoldNoteBody.cs
index ac3792c01d..1f290f1f1c 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/IHoldNoteBody.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Default/IHoldNoteBody.cs
@@ -1,7 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Mania.Skinning.Default
{
///
/// Interface for mania hold note bodies.
diff --git a/osu.Game.Rulesets.Mania/Skinning/HitTargetInsetContainer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs
similarity index 96%
rename from osu.Game.Rulesets.Mania/Skinning/HitTargetInsetContainer.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs
index c8b05ed2f8..3c89e2c04a 100644
--- a/osu.Game.Rulesets.Mania/Skinning/HitTargetInsetContainer.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class HitTargetInsetContainer : Container
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyBodyPiece.cs
similarity index 99%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyBodyPiece.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyBodyPiece.cs
index 8902d82f33..31db08ce2f 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyBodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyBodyPiece.cs
@@ -14,7 +14,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyBodyPiece : LegacyManiaColumnElement
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs
similarity index 98%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs
index 3bf51b3073..661e7f66f4 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs
@@ -12,7 +12,7 @@ using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyColumnBackground : LegacyManiaColumnElement, IKeyBindingHandler
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs
similarity index 98%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs
index 7c5d41efcf..73aece1ed4 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs
@@ -13,7 +13,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyHitExplosion : LegacyManiaColumnElement, IHitExplosion
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHitTarget.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs
similarity index 98%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyHitTarget.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs
index 6eced571d2..490a03d11a 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyHitTarget.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs
@@ -12,7 +12,7 @@ using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyHitTarget : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteHeadPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteHeadPiece.cs
similarity index 92%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteHeadPiece.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteHeadPiece.cs
index c5aa062d0f..21e5bdd5d6 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteHeadPiece.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteHeadPiece.cs
@@ -4,7 +4,7 @@
using osu.Framework.Graphics.Textures;
using osu.Game.Skinning;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyHoldNoteHeadPiece : LegacyNotePiece
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteTailPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteTailPiece.cs
similarity index 96%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteTailPiece.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteTailPiece.cs
index 2e8259f10a..232b47ae27 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteTailPiece.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteTailPiece.cs
@@ -6,7 +6,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyHoldNoteTailPiece : LegacyNotePiece
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs
similarity index 98%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyKeyArea.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs
index b269ea25d4..78ccb83a8c 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyKeyArea.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs
@@ -12,7 +12,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyKeyArea : LegacyManiaColumnElement, IKeyBindingHandler
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyManiaColumnElement.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs
similarity index 96%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyManiaColumnElement.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs
index 3c0c632c14..eb5514ba43 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyManiaColumnElement.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs
@@ -8,7 +8,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Skinning;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
///
/// A which is placed somewhere within a .
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs
similarity index 98%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyNotePiece.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs
index 283b04373b..31279796ce 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyNotePiece.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs
@@ -12,7 +12,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyNotePiece : LegacyManiaColumnElement
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs
similarity index 99%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs
index b0bab8e760..fec3e9493e 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs
@@ -12,7 +12,7 @@ using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyStageBackground : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyStageForeground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs
similarity index 97%
rename from osu.Game.Rulesets.Mania/Skinning/LegacyStageForeground.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs
index 4609fcc849..4e1952a670 100644
--- a/osu.Game.Rulesets.Mania/Skinning/LegacyStageForeground.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyStageForeground : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs
similarity index 99%
rename from osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs
rename to osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs
index 3724269f4d..89f639e2fe 100644
--- a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs
@@ -2,19 +2,19 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osu.Framework.Graphics;
-using osu.Framework.Bindables;
-using osu.Game.Rulesets.Scoring;
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Mania.Beatmaps;
-using osu.Game.Skinning;
using System.Collections.Generic;
using System.Diagnostics;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
using osu.Game.Audio;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Objects.Legacy;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Skinning;
-namespace osu.Game.Rulesets.Mania.Skinning
+namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class ManiaLegacySkinTransformer : LegacySkinTransformer
{
diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs
index e0b099ab9b..ec6c377a2e 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK.Graphics;
diff --git a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs
index 225269cf48..69b81d6d5c 100644
--- a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs
+++ b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Utils;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK;
using osuTK.Graphics;
diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs
index 7b909d2907..7df5ca0f7c 100644
--- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs
@@ -13,7 +13,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests.Mods
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs
index dde02e873b..fefe983f97 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs
@@ -12,7 +12,7 @@ using osu.Framework.Graphics.OpenGL.Textures;
using osu.Framework.Graphics.Textures;
using osu.Framework.Testing.Input;
using osu.Game.Audio;
-using osu.Game.Rulesets.Osu.Skinning;
+using osu.Game.Rulesets.Osu.Skinning.Legacy;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs
index 084af7dafe..aac6db60fe 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs
@@ -12,7 +12,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs
index b71400b71d..e111bb1054 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs
@@ -19,7 +19,7 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Storyboards;
using osuTK;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index fff033357d..e8ac60bc5e 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -12,5 +12,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
public double ApproachRate;
public double OverallDifficulty;
public int HitCircleCount;
+ public int SpinnerCount;
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 6027635b75..6a7d76151c 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -48,6 +48,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1);
int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
+ int spinnerCount = beatmap.HitObjects.Count(h => h is Spinner);
return new OsuDifficultyAttributes
{
@@ -59,6 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
OverallDifficulty = (80 - hitWindowGreat) / 6,
MaxCombo = maxCombo,
HitCircleCount = hitCirclesCount,
+ SpinnerCount = spinnerCount,
Skills = skills
};
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index 063cde8747..030b0cf6d1 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
multiplier *= 0.90;
if (mods.Any(m => m is OsuModSpunOut))
- multiplier *= 0.95;
+ multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85);
double aimValue = computeAimValue();
double speedValue = computeSpeedValue();
@@ -99,16 +99,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (Attributes.MaxCombo > 0)
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
- double approachRateFactor = 1.0;
-
+ double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
- approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33);
+ approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33);
else if (Attributes.ApproachRate < 8.0)
- {
- approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate);
- }
+ approachRateFactor += 0.1 * (8.0 - Attributes.ApproachRate);
- aimValue *= approachRateFactor;
+ aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0));
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
if (mods.Any(h => h is OsuModHidden))
@@ -137,8 +134,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0;
// Longer maps are worth more
- speedValue *= 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
- (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
+ double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
+ (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
+ speedValue *= lengthBonus;
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
speedValue *= Math.Pow(0.97, countMiss);
@@ -147,11 +145,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (Attributes.MaxCombo > 0)
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
- double approachRateFactor = 1.0;
+ double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33)
- approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33);
+ approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33);
- speedValue *= approachRateFactor;
+ speedValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0));
if (mods.Any(m => m is OsuModHidden))
speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
index 2868ddeaa4..0cfc67cedb 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
index 5581ce4bfd..1c3d270c95 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
@@ -5,7 +5,7 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK;
using osuTK.Graphics;
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs
index 2347d8a34c..92961b40bc 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
index b7e60295cb..df0a41455f 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
namespace osu.Game.Rulesets.Osu.Mods
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index abb51ae420..3c0260f5f5 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -12,7 +12,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osuTK;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index af5b609ec8..511cbc2347 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -7,13 +7,13 @@ using JetBrains.Annotations;
using osuTK;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Skinning;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Osu.UI;
using osuTK.Graphics;
using osu.Game.Skinning;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
index a684df98cb..76490e0de1 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
@@ -9,7 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Skinning;
using osuTK;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index aea37acf6f..1f3bcece0c 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -15,8 +15,8 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Osu.Skinning;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index d8180b0e58..cba0c5be14 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -24,13 +24,13 @@ using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Difficulty;
using osu.Game.Rulesets.Osu.Scoring;
-using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Skinning;
using System;
using System.Linq;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Skinning.Legacy;
using osu.Game.Rulesets.Osu.Statistics;
using osu.Game.Screens.Ranking.Statistics;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/ApproachCircle.cs
similarity index 96%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/ApproachCircle.cs
index 1b474f265c..62f00a2b49 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/ApproachCircle.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class ApproachCircle : Container
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/CirclePiece.cs
similarity index 95%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/CirclePiece.cs
index d0e1055dce..ba41ebd445 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/CirclePiece.cs
@@ -7,9 +7,10 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class CirclePiece : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs
similarity index 98%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs
index 14ce3b014d..667fee1495 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs
@@ -10,10 +10,12 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class DefaultSpinnerDisc : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DrawableSliderPath.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DrawableSliderPath.cs
similarity index 96%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DrawableSliderPath.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/DrawableSliderPath.cs
index c31d6beb01..db077f009d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DrawableSliderPath.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/DrawableSliderPath.cs
@@ -4,7 +4,7 @@
using osu.Framework.Graphics.Lines;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public abstract class DrawableSliderPath : SmoothPath
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/ExplodePiece.cs
similarity index 94%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/ExplodePiece.cs
index 09299a3622..510ed225a8 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/ExplodePiece.cs
@@ -5,9 +5,10 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class ExplodePiece : Container
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/FlashPiece.cs
similarity index 90%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/FlashPiece.cs
index 038a2299e9..06ee64d8b3 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/FlashPiece.cs
@@ -3,10 +3,11 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osuTK;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Osu.Objects;
+using osuTK;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class FlashPiece : Container
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/GlowPiece.cs
similarity index 94%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/GlowPiece.cs
index 30937313fd..f5e01b802e 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/GlowPiece.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class GlowPiece : Container
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs
similarity index 96%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs
index 102166f8dd..fcbe4c1b28 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs
@@ -6,10 +6,12 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class MainCirclePiece : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ManualSliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs
similarity index 90%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ManualSliderBody.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs
index d69df1d5c2..d73c94eb9b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ManualSliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs
@@ -4,7 +4,7 @@
using System.Collections.Generic;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
///
/// A with the ability to set the drawn vertices manually.
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/NumberPiece.cs
similarity index 96%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/NumberPiece.cs
index 7c94568835..bea6186501 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/NumberPiece.cs
@@ -5,12 +5,12 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
-using osu.Game.Graphics.Sprites;
-using osuTK.Graphics;
using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
using osu.Game.Skinning;
+using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class NumberPiece : Container
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs
similarity index 93%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs
index 29dff53f54..e77c93c721 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs
@@ -5,11 +5,12 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Configuration;
-using osu.Game.Rulesets.Osu.Skinning;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Skinning;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public abstract class PlaySliderBody : SnakingSliderBody
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/ReverseArrowPiece.cs
similarity index 94%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/ReverseArrowPiece.cs
index ae43006e76..0009ffc586 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/ReverseArrowPiece.cs
@@ -1,17 +1,18 @@
// 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.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
-using osuTK;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
-using osu.Game.Skinning;
-using osu.Framework.Allocation;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Skinning;
+using osuTK;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class ReverseArrowPiece : BeatSyncedContainer
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/RingPiece.cs
similarity index 91%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/RingPiece.cs
index 619fea73bc..7f10a7bf56 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/RingPiece.cs
@@ -3,11 +3,12 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Osu.Objects;
using osuTK;
using osuTK.Graphics;
-using osu.Framework.Graphics.Shapes;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class RingPiece : CircularContainer
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs
similarity index 98%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs
index ca5ca7ac59..a96beb66d4 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs
@@ -10,15 +10,16 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Input.Events;
+using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
-using osu.Game.Rulesets.Osu.Skinning;
-using osuTK.Graphics;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
-using osu.Game.Graphics;
+using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition, IHasAccentColour
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBody.cs
similarity index 98%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SliderBody.cs
index 8758a4a066..7e6df759f8 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBody.cs
@@ -9,7 +9,7 @@ using osu.Framework.Graphics.Lines;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public abstract class SliderBody : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs
similarity index 97%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs
index 8835a0d84a..ed4e04184b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs
@@ -8,9 +8,11 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
///
/// A which changes its curve depending on the snaking progress.
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerBackgroundLayer.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerBackgroundLayer.cs
similarity index 85%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerBackgroundLayer.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SpinnerBackgroundLayer.cs
index 3cd2454706..f8a6e1d3c9 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerBackgroundLayer.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerBackgroundLayer.cs
@@ -5,9 +5,8 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class SpinnerBackgroundLayer : SpinnerFill
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBonusDisplay.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerBonusDisplay.cs
similarity index 94%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBonusDisplay.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SpinnerBonusDisplay.cs
index f483bb1b26..c0db6228ef 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBonusDisplay.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerBonusDisplay.cs
@@ -5,8 +5,9 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets.Osu.Objects;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
///
/// Shows incremental bonus score achieved for a spinner.
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerCentreLayer.cs
similarity index 95%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SpinnerCentreLayer.cs
index b62ce822f0..67b5ed5410 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerCentreLayer.cs
@@ -9,11 +9,11 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class SpinnerCentreLayer : CompositeDrawable, IHasAccentColour
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerFill.cs
similarity index 96%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SpinnerFill.cs
index 043bc5618c..f574ae589e 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerFill.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class SpinnerFill : CircularContainer, IHasAccentColour
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs
similarity index 97%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs
index f82003edb8..9393a589eb 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs
@@ -9,10 +9,11 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Play;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class SpinnerRotationTracker : CircularContainer
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs
similarity index 97%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs
index 80ab03c45c..e5952ecf97 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs
@@ -10,7 +10,7 @@ using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class SpinnerSpmCounter : Container
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerTicks.cs
similarity index 97%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/SpinnerTicks.cs
index ba7e8eae6f..e518ae1da8 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerTicks.cs
@@ -7,12 +7,12 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
-using osuTK;
-using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osuTK;
+using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class SpinnerTicks : Container, IHasAccentColour
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/TrianglesPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs
similarity index 92%
rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/TrianglesPiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs
index 53dc7ecea3..fa23c60d57 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/TrianglesPiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs
@@ -3,7 +3,7 @@
using osu.Game.Graphics.Backgrounds;
-namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class TrianglesPiece : Triangles
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs
similarity index 96%
rename from osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs
rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs
index e96bd29ad5..314139d02a 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs
@@ -3,11 +3,11 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Game.Skinning;
using osu.Game.Rulesets.Osu.UI.Cursor;
+using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Skinning
+namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
public class LegacyCursor : OsuCursorSprite
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs
similarity index 97%
rename from osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs
rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs
index e6cd7bc59d..f18d3191ca 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs
@@ -10,7 +10,7 @@ using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Skinning;
-namespace osu.Game.Rulesets.Osu.Skinning
+namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
public class LegacyCursorTrail : CursorTrail
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs
similarity index 99%
rename from osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs
rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs
index 21af9a479e..545e80a709 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs
@@ -17,7 +17,7 @@ using osuTK;
using osuTK.Graphics;
using static osu.Game.Skinning.LegacySkinConfiguration;
-namespace osu.Game.Rulesets.Osu.Skinning
+namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
public class LegacyMainCirclePiece : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs
similarity index 99%
rename from osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs
rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs
index 05f4c8e307..efeca53969 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs
@@ -13,7 +13,7 @@ using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Skinning
+namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
///
/// Legacy skinned spinner with two main spinning layers, one fixed overlay and one final spinning overlay.
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs
similarity index 99%
rename from osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs
rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs
index fba802f085..4e07cb60b3 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs
@@ -13,7 +13,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Skinning
+namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
///
/// Legacy skinned spinner with one main spinning layer and a background layer.
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs
similarity index 97%
rename from osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs
index 836069013d..1a8c5ada1b 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Game.Skinning;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Skinning
+namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
public class LegacySliderBall : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs
similarity index 96%
rename from osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs
rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs
index aad8b189d9..744ded37c9 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs
@@ -5,10 +5,10 @@ using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Utils;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Osu.Skinning
+namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
public class LegacySliderBody : PlaySliderBody
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs
similarity index 98%
rename from osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs
rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs
index 5aa136cf7e..ec7ecb0d28 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs
@@ -12,7 +12,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Skinning
+namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
public abstract class LegacySpinner : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs
similarity index 99%
rename from osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
rename to osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs
index 78bc26eff7..d74f885573 100644
--- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Osu.Skinning
+namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
public class OsuLegacySkinTransformer : LegacySkinTransformer
{
diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs
index 114038b81c..4ae3cbd418 100644
--- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs
@@ -6,7 +6,7 @@ using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Skinning;
+using osu.Game.Rulesets.Taiko.Skinning.Legacy;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
index c596fa2c7c..4ead4982a1 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
@@ -8,12 +8,12 @@ using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osuTK.Graphics;
-using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning;
using osuTK;
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
index be659f6ca5..e68e40ae1c 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
@@ -4,7 +4,7 @@
using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index 29a96a7a40..d1751d8a75 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -11,7 +11,7 @@ using osu.Framework.Graphics;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index ff0a27023d..5c6278ed08 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -13,7 +13,7 @@ using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs
index 6202583494..14c86d151f 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CentreHitCirclePiece.cs
similarity index 96%
rename from osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitCirclePiece.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Default/CentreHitCirclePiece.cs
index 0509841ba8..f65bb54726 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CentreHitCirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CentreHitCirclePiece.cs
@@ -4,11 +4,11 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osuTK;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osuTK;
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Taiko.Skinning.Default
{
public class CentreHitCirclePiece : CirclePiece
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs
similarity index 98%
rename from osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs
index f515a35c18..8ca996159b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs
@@ -5,15 +5,15 @@ using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics.Backgrounds;
-using osuTK.Graphics;
-using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Graphics.Effects;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
+using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
+using osuTK.Graphics;
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Taiko.Skinning.Default
{
///
/// A circle piece which is used uniformly through osu!taiko to visualise hitobjects.
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/ElongatedCirclePiece.cs
similarity index 92%
rename from osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Default/ElongatedCirclePiece.cs
index 034ab6dd21..210841bca0 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/ElongatedCirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Default/ElongatedCirclePiece.cs
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Taiko.Skinning.Default
{
public class ElongatedCirclePiece : CirclePiece
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/RimHitCirclePiece.cs
similarity index 96%
rename from osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitCirclePiece.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Default/RimHitCirclePiece.cs
index 3273ab7fa7..ca2ab301be 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/RimHitCirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Default/RimHitCirclePiece.cs
@@ -9,7 +9,7 @@ using osu.Game.Graphics;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Taiko.Skinning.Default
{
public class RimHitCirclePiece : CirclePiece
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/SwellSymbolPiece.cs
similarity index 95%
rename from osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Default/SwellSymbolPiece.cs
index a8f9f0b94d..2f59cac3ff 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/SwellSymbolPiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Default/SwellSymbolPiece.cs
@@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
+using osuTK;
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Taiko.Skinning.Default
{
public class SwellCirclePiece : CirclePiece
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/TickPiece.cs
similarity index 96%
rename from osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Default/TickPiece.cs
index 0648bcebcd..09c8243aac 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/TickPiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Default/TickPiece.cs
@@ -3,11 +3,11 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
using osuTK;
using osuTK.Graphics;
-using osu.Framework.Graphics.Shapes;
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
+namespace osu.Game.Rulesets.Taiko.Skinning.Default
{
public class TickPiece : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyBarLine.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyBarLine.cs
similarity index 93%
rename from osu.Game.Rulesets.Taiko/Skinning/LegacyBarLine.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyBarLine.cs
index 7d08a21ab1..2b528ae8ce 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/LegacyBarLine.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyBarLine.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
public class LegacyBarLine : Sprite
{
diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs
similarity index 98%
rename from osu.Game.Rulesets.Taiko/Skinning/LegacyCirclePiece.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs
index 9b73ccd248..821ddc3c04 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/LegacyCirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs
@@ -12,7 +12,7 @@ using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
public class LegacyCirclePiece : CompositeDrawable, IHasAccentColour
{
diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyDrumRoll.cs
similarity index 97%
rename from osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyDrumRoll.cs
index 5ab8e3a8c8..ea6f813be8 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/LegacyDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyDrumRoll.cs
@@ -10,7 +10,7 @@ using osu.Game.Graphics;
using osu.Game.Skinning;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
public class LegacyDrumRoll : CompositeDrawable, IHasAccentColour
{
diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHit.cs
similarity index 94%
rename from osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHit.cs
index b11b64c22c..d93317f0e2 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHit.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHit.cs
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Game.Skinning;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
public class LegacyHit : LegacyCirclePiece
{
diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs
similarity index 98%
rename from osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs
index 19493271be..651cdd6438 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
public class LegacyHitExplosion : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs
similarity index 99%
rename from osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs
index b7b55b9ae0..795885d4b9 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs
@@ -11,7 +11,7 @@ using osu.Game.Rulesets.Taiko.Audio;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
///
/// A component of the playfield that captures input and displays input as a drum.
diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs
similarity index 98%
rename from osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs
index e029040ef3..eb92097204 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs
@@ -13,7 +13,7 @@ using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
public class LegacyTaikoScroller : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs
similarity index 97%
rename from osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs
index e522fb7c10..9feb2054da 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
public class TaikoLegacyHitTarget : CompositeDrawable
{
diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyPlayfieldBackgroundRight.cs
similarity index 97%
rename from osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyPlayfieldBackgroundRight.cs
index 4bbb6be6b1..02756d57a4 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyPlayfieldBackgroundRight.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyPlayfieldBackgroundRight.cs
@@ -10,7 +10,7 @@ using osu.Game.Graphics.Containers;
using osu.Game.Skinning;
using osuTK;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
public class TaikoLegacyPlayfieldBackgroundRight : BeatSyncedContainer
{
diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs
similarity index 99%
rename from osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs
rename to osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs
index 96fb065e79..d8e3100048 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs
@@ -11,7 +11,7 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Skinning;
-namespace osu.Game.Rulesets.Taiko.Skinning
+namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
public class TaikoLegacySkinTransformer : LegacySkinTransformer
{
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index 2a49dd655c..f2b5d195b4 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -25,7 +25,7 @@ using System.Linq;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Taiko.Edit;
using osu.Game.Rulesets.Taiko.Objects;
-using osu.Game.Rulesets.Taiko.Skinning;
+using osu.Game.Rulesets.Taiko.Skinning.Legacy;
using osu.Game.Screens.Ranking.Statistics;
using osu.Game.Skinning;
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index b941313103..c32e359de6 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -14,6 +14,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
+using osu.Game.Database;
using osu.Game.IO;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
@@ -68,6 +69,42 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
+ [Test]
+ public async Task TestImportThenDeleteFromStream()
+ {
+ // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
+ {
+ try
+ {
+ var osu = LoadOsuIntoHost(host);
+
+ var tempPath = TestResources.GetTestBeatmapForImport();
+
+ var manager = osu.Dependencies.Get();
+
+ BeatmapSetInfo importedSet;
+
+ using (var stream = File.OpenRead(tempPath))
+ {
+ importedSet = await manager.Import(new ImportTask(stream, Path.GetFileName(tempPath)));
+ ensureLoaded(osu);
+ }
+
+ Assert.IsTrue(File.Exists(tempPath), "Stream source file somehow went missing");
+ File.Delete(tempPath);
+
+ var imported = manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID);
+
+ deleteBeatmapSet(imported, osu);
+ }
+ finally
+ {
+ host.Exit();
+ }
+ }
+ }
+
[Test]
public async Task TestImportThenImport()
{
@@ -127,7 +164,7 @@ namespace osu.Game.Tests.Beatmaps.IO
// zip files differ because different compression or encoder.
Assert.AreNotEqual(hashBefore, hashFile(temp));
- var importedSecondTime = await osu.Dependencies.Get().Import(temp);
+ var importedSecondTime = await osu.Dependencies.Get().Import(new ImportTask(temp));
ensureLoaded(osu);
@@ -184,7 +221,7 @@ namespace osu.Game.Tests.Beatmaps.IO
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
- var importedSecondTime = await osu.Dependencies.Get().Import(temp);
+ var importedSecondTime = await osu.Dependencies.Get().Import(new ImportTask(temp));
ensureLoaded(osu);
@@ -235,7 +272,7 @@ namespace osu.Game.Tests.Beatmaps.IO
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
- var importedSecondTime = await osu.Dependencies.Get().Import(temp);
+ var importedSecondTime = await osu.Dependencies.Get().Import(new ImportTask(temp));
ensureLoaded(osu);
@@ -351,7 +388,7 @@ namespace osu.Game.Tests.Beatmaps.IO
// this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu.
try
{
- await manager.Import(breakTemp);
+ await manager.Import(new ImportTask(breakTemp));
}
catch
{
@@ -614,7 +651,7 @@ namespace osu.Game.Tests.Beatmaps.IO
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
- var imported = await osu.Dependencies.Get().Import(temp);
+ var imported = await osu.Dependencies.Get().Import(new ImportTask(temp));
ensureLoaded(osu);
@@ -667,7 +704,7 @@ namespace osu.Game.Tests.Beatmaps.IO
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
- var imported = await osu.Dependencies.Get().Import(temp);
+ var imported = await osu.Dependencies.Get().Import(new ImportTask(temp));
ensureLoaded(osu);
@@ -821,7 +858,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get();
- var importedSet = await manager.Import(temp);
+ var importedSet = await manager.Import(new ImportTask(temp));
ensureLoaded(osu);
diff --git a/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs b/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs
new file mode 100644
index 0000000000..b90382488f
--- /dev/null
+++ b/osu.Game.Tests/Input/ConfineMouseTrackerTest.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 NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Input;
+using osu.Framework.Testing;
+using osu.Game.Configuration;
+using osu.Game.Input;
+using osu.Game.Tests.Visual.Navigation;
+
+namespace osu.Game.Tests.Input
+{
+ [HeadlessTest]
+ public class ConfineMouseTrackerTest : OsuGameTestScene
+ {
+ [Resolved]
+ private FrameworkConfigManager frameworkConfigManager { get; set; }
+
+ [Resolved]
+ private OsuConfigManager osuConfigManager { get; set; }
+
+ [TestCase(WindowMode.Windowed)]
+ [TestCase(WindowMode.Borderless)]
+ public void TestDisableConfining(WindowMode windowMode)
+ {
+ setWindowModeTo(windowMode);
+ setGameSideModeTo(OsuConfineMouseMode.Never);
+
+ setLocalUserPlayingTo(false);
+ frameworkSideModeIs(ConfineMouseMode.Never);
+
+ setLocalUserPlayingTo(true);
+ frameworkSideModeIs(ConfineMouseMode.Never);
+ }
+
+ [TestCase(WindowMode.Windowed)]
+ [TestCase(WindowMode.Borderless)]
+ public void TestConfiningDuringGameplay(WindowMode windowMode)
+ {
+ setWindowModeTo(windowMode);
+ setGameSideModeTo(OsuConfineMouseMode.DuringGameplay);
+
+ setLocalUserPlayingTo(false);
+ frameworkSideModeIs(ConfineMouseMode.Never);
+
+ setLocalUserPlayingTo(true);
+ frameworkSideModeIs(ConfineMouseMode.Always);
+ }
+
+ [TestCase(WindowMode.Windowed)]
+ [TestCase(WindowMode.Borderless)]
+ public void TestConfineAlwaysUserSetting(WindowMode windowMode)
+ {
+ setWindowModeTo(windowMode);
+ setGameSideModeTo(OsuConfineMouseMode.Always);
+
+ setLocalUserPlayingTo(false);
+ frameworkSideModeIs(ConfineMouseMode.Always);
+
+ setLocalUserPlayingTo(true);
+ frameworkSideModeIs(ConfineMouseMode.Always);
+ }
+
+ [Test]
+ public void TestConfineAlwaysInFullscreen()
+ {
+ setGameSideModeTo(OsuConfineMouseMode.Never);
+
+ setWindowModeTo(WindowMode.Fullscreen);
+
+ setLocalUserPlayingTo(false);
+ frameworkSideModeIs(ConfineMouseMode.Fullscreen);
+
+ setLocalUserPlayingTo(true);
+ frameworkSideModeIs(ConfineMouseMode.Fullscreen);
+
+ setWindowModeTo(WindowMode.Windowed);
+
+ // old state is restored
+ gameSideModeIs(OsuConfineMouseMode.Never);
+ frameworkSideModeIs(ConfineMouseMode.Never);
+ }
+
+ private void setWindowModeTo(WindowMode mode)
+ // needs to go through .GetBindable().Value instead of .Set() due to default overrides
+ => AddStep($"make window {mode}", () => frameworkConfigManager.GetBindable(FrameworkSetting.WindowMode).Value = mode);
+
+ private void setGameSideModeTo(OsuConfineMouseMode mode)
+ => AddStep($"set {mode} game-side", () => Game.LocalConfig.Set(OsuSetting.ConfineMouseMode, mode));
+
+ private void setLocalUserPlayingTo(bool playing)
+ => AddStep($"local user {(playing ? "playing" : "not playing")}", () => Game.LocalUserPlaying.Value = playing);
+
+ private void gameSideModeIs(OsuConfineMouseMode mode)
+ => AddAssert($"mode is {mode} game-side", () => Game.LocalConfig.Get(OsuSetting.ConfineMouseMode) == mode);
+
+ private void frameworkSideModeIs(ConfineMouseMode mode)
+ => AddAssert($"mode is {mode} framework-side", () => frameworkConfigManager.Get(FrameworkSetting.ConfineMouseMode) == mode);
+ }
+}
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
index b34e027e9c..aaf3323432 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
@@ -6,6 +6,7 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Database;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
using osu.Game.Tests.Resources;
@@ -52,7 +53,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("import beatmap with track", () =>
{
- var setWithTrack = Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).Result;
+ var setWithTrack = Game.BeatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).Result;
Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(setWithTrack.Beatmaps.First());
});
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeFilterControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftFilterControl.cs
similarity index 55%
rename from osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeFilterControl.cs
rename to osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftFilterControl.cs
index 7c0c2797f5..f635a28b5c 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeFilterControl.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftFilterControl.cs
@@ -6,14 +6,17 @@ using osu.Game.Screens.Multi.Lounge.Components;
namespace osu.Game.Tests.Visual.Multiplayer
{
- public class TestSceneLoungeFilterControl : OsuTestScene
+ public class TestSceneTimeshiftFilterControl : OsuTestScene
{
- public TestSceneLoungeFilterControl()
+ public TestSceneTimeshiftFilterControl()
{
- Child = new FilterControl
+ Child = new TimeshiftFilterControl
{
Anchor = Anchor.Centre,
- Origin = Anchor.Centre
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ Width = 0.7f,
+ Height = 80,
};
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
index fca642ad6c..9d0be85a3a 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -208,6 +208,46 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any());
}
+ [Test]
+ public void TestCtrlWShortcut()
+ {
+ AddStep("Join 2 channels", () =>
+ {
+ channelManager.JoinChannel(channel1);
+ channelManager.JoinChannel(channel2);
+ });
+
+ // Want to close channel 2
+ AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2]));
+
+ pressControlW();
+ // Channel 2 should be closed
+ AddAssert("channel 1 open", () => channelManager.JoinedChannels.Contains(channel1));
+ AddAssert("channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2));
+
+ // Want to close channel 1
+ AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
+
+ pressControlW();
+ // Channel 1 and channel 2 should be closed
+ AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any());
+ }
+
+ [Test]
+ public void TestCtrlTShortcut()
+ {
+ AddStep("Join 2 channels", () =>
+ {
+ channelManager.JoinChannel(channel1);
+ channelManager.JoinChannel(channel2);
+ });
+
+ // Want to join another channel
+ pressControlT();
+ // Selector should be visible
+ AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
+ }
+
private void pressChannelHotkey(int number)
{
var channelKey = Key.Number0 + number;
@@ -216,6 +256,20 @@ namespace osu.Game.Tests.Visual.Online
InputManager.ReleaseKey(Key.AltLeft);
}
+ private void pressControlW()
+ {
+ AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft));
+ AddStep("Press W", () => InputManager.Key(Key.W));
+ AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft));
+ }
+
+ private void pressControlT()
+ {
+ AddStep("Press and hold Control", () => InputManager.PressKey(Key.ControlLeft));
+ AddStep("Press T", () => InputManager.Key(Key.T));
+ AddStep("Release Control", () => InputManager.ReleaseKey(Key.ControlLeft));
+ }
+
private void clickDrawable(Drawable d)
{
InputManager.MoveMouseTo(d);
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
index 684ce10820..63bda08c88 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Online
ensureSoleilyRemoved();
createButtonWithBeatmap(createSoleily());
AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded);
- AddStep("import soleily", () => beatmaps.Import(new[] { TestResources.GetTestBeatmapForImport() }));
+ AddStep("import soleily", () => beatmaps.Import(TestResources.GetTestBeatmapForImport()));
AddUntilStep("wait for beatmap import", () => beatmaps.GetAllUsableBeatmapSets().Any(b => b.OnlineBeatmapSetID == 241526));
createButtonWithBeatmap(createSoleily());
AddAssert("button state downloaded", () => downloadButton.DownloadState == DownloadState.LocallyAvailable);
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
index e54292f7cc..81862448a8 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
@@ -12,6 +12,7 @@ using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
+using osu.Game.Database;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Leaderboards;
@@ -83,7 +84,7 @@ namespace osu.Game.Tests.Visual.UserInterface
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), dependencies.Get(), Beatmap.Default));
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory));
- beatmap = beatmapManager.Import(TestResources.GetTestBeatmapForImport()).Result.Beatmaps[0];
+ beatmap = beatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).Result.Beatmaps[0];
for (int i = 0; i < 50; i++)
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
index 645b83758c..8614700b15 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value);
AddStep("open Customisation", () => modSelect.CustomiseButton.Click());
AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableMod));
- AddAssert("controls hidden", () => modSelect.ModSettingsContainer.Alpha == 0);
+ AddAssert("controls hidden", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden);
}
[Test]
@@ -72,11 +72,11 @@ namespace osu.Game.Tests.Visual.UserInterface
createModSelect();
openModSelect();
- AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.Alpha == 0);
+ AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden);
AddStep("select mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod));
- AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.Alpha == 1);
+ AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.State.Value == Visibility.Visible);
AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod));
- AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.Alpha == 0);
+ AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden);
}
[Test]
@@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("change mod settings menu width to full screen", () => modSelect.SetModSettingsWidth(1.0f));
AddStep("select cm2", () => modSelect.SelectMod(testCustomisableAutoOpenMod));
- AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.Alpha == 1);
+ AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.State.Value == Visibility.Visible);
AddStep("hover over mod behind settings menu", () => InputManager.MoveMouseTo(modSelect.GetModButton(testCustomisableMod)));
AddAssert("Mod is not considered hovered over", () => !modSelect.GetModButton(testCustomisableMod).IsHovered);
AddStep("left click mod", () => InputManager.Click(MouseButton.Left));
@@ -153,7 +153,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private class TestModSelectOverlay : ModSelectOverlay
{
- public new Container ModSettingsContainer => base.ModSettingsContainer;
+ public new VisibilityContainer ModSettingsContainer => base.ModSettingsContainer;
public new TriangleButton CustomiseButton => base.CustomiseButton;
public bool ButtonsLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded);
diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs
index c4563d5844..e90ccbb805 100644
--- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs
+++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs
@@ -104,7 +104,7 @@ namespace osu.Game.Beatmaps
string cacheFilePath = storage.GetFullPath(cache_database_name);
string compressedCacheFilePath = $"{cacheFilePath}.bz2";
- cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2");
+ cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2?{DateTimeOffset.UtcNow:yyyyMMdd}");
cacheDownloadRequest.Failed += ex =>
{
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 8bdc804311..36cc4cce39 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -21,9 +21,7 @@ using osu.Game.IO;
using osu.Game.IO.Archives;
using osu.Game.IPC;
using osu.Game.Overlays.Notifications;
-using osu.Game.Utils;
using SharpCompress.Archives.Zip;
-using SharpCompress.Common;
using FileInfo = osu.Game.IO.FileInfo;
namespace osu.Game.Database
@@ -114,10 +112,19 @@ namespace osu.Game.Database
PostNotification?.Invoke(notification);
- return Import(notification, paths);
+ return Import(notification, paths.Select(p => new ImportTask(p)).ToArray());
}
- protected async Task> Import(ProgressNotification notification, params string[] paths)
+ public Task Import(Stream stream, string filename)
+ {
+ var notification = new ProgressNotification { State = ProgressNotificationState.Active };
+
+ PostNotification?.Invoke(notification);
+
+ return Import(notification, new ImportTask(stream, filename));
+ }
+
+ protected async Task> Import(ProgressNotification notification, params ImportTask[] tasks)
{
notification.Progress = 0;
notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising...";
@@ -126,13 +133,13 @@ namespace osu.Game.Database
var imported = new List();
- await Task.WhenAll(paths.Select(async path =>
+ await Task.WhenAll(tasks.Select(async task =>
{
notification.CancellationToken.ThrowIfCancellationRequested();
try
{
- var model = await Import(path, notification.CancellationToken);
+ var model = await Import(task, notification.CancellationToken);
lock (imported)
{
@@ -140,8 +147,8 @@ namespace osu.Game.Database
imported.Add(model);
current++;
- notification.Text = $"Imported {current} of {paths.Length} {HumanisedModelName}s";
- notification.Progress = (float)current / paths.Length;
+ notification.Text = $"Imported {current} of {tasks.Length} {HumanisedModelName}s";
+ notification.Progress = (float)current / tasks.Length;
}
}
catch (TaskCanceledException)
@@ -150,7 +157,7 @@ namespace osu.Game.Database
}
catch (Exception e)
{
- Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})", LoggingTarget.Database);
+ Logger.Error(e, $@"Could not import ({task})", LoggingTarget.Database);
}
}));
@@ -183,16 +190,17 @@ namespace osu.Game.Database
///
/// Import one from the filesystem and delete the file on success.
+ /// Note that this bypasses the UI flow and should only be used for special cases or testing.
///
- /// The archive location on disk.
+ /// The containing data about the to import.
/// An optional cancellation token.
/// The imported model, if successful.
- public async Task Import(string path, CancellationToken cancellationToken = default)
+ internal async Task Import(ImportTask task, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
TModel import;
- using (ArchiveReader reader = getReaderFrom(path))
+ using (ArchiveReader reader = task.GetReader())
import = await Import(reader, cancellationToken);
// We may or may not want to delete the file depending on where it is stored.
@@ -201,12 +209,12 @@ namespace osu.Game.Database
// TODO: Add a check to prevent files from storage to be deleted.
try
{
- if (import != null && File.Exists(path) && ShouldDeleteArchive(path))
- File.Delete(path);
+ if (import != null && File.Exists(task.Path) && ShouldDeleteArchive(task.Path))
+ File.Delete(task.Path);
}
catch (Exception e)
{
- LogForModel(import, $@"Could not delete original file after import ({Path.GetFileName(path)})", e);
+ LogForModel(import, $@"Could not delete original file after import ({task})", e);
}
return import;
@@ -727,23 +735,6 @@ namespace osu.Game.Database
protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}";
- ///
- /// Creates an from a valid storage path.
- ///
- /// A file or folder path resolving the archive content.
- /// A reader giving access to the archive's content.
- private ArchiveReader getReaderFrom(string path)
- {
- if (ZipUtils.IsZipArchive(path))
- return new ZipArchiveReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read), Path.GetFileName(path));
- if (Directory.Exists(path))
- return new LegacyDirectoryArchiveReader(path);
- if (File.Exists(path))
- return new LegacyFileArchiveReader(path);
-
- throw new InvalidFormatException($"{path} is not a valid archive");
- }
-
#region Event handling / delaying
private readonly List queuedEvents = new List();
diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs
index 8f469ca590..50b022f9ff 100644
--- a/osu.Game/Database/DownloadableArchiveModelManager.cs
+++ b/osu.Game/Database/DownloadableArchiveModelManager.cs
@@ -82,7 +82,7 @@ namespace osu.Game.Database
Task.Factory.StartNew(async () =>
{
// This gets scheduled back to the update thread, but we want the import to run in the background.
- var imported = await Import(notification, filename);
+ var imported = await Import(notification, new ImportTask(filename));
// for now a failed import will be marked as a failed download for simplicity.
if (!imported.Any())
diff --git a/osu.Game/Database/ICanAcceptFiles.cs b/osu.Game/Database/ICanAcceptFiles.cs
index e4d92d957c..276c284c9f 100644
--- a/osu.Game/Database/ICanAcceptFiles.cs
+++ b/osu.Game/Database/ICanAcceptFiles.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using System.IO;
using System.Threading.Tasks;
namespace osu.Game.Database
@@ -17,6 +18,13 @@ namespace osu.Game.Database
/// The files which should be imported.
Task Import(params string[] paths);
+ ///
+ /// Import the provided stream as a simple item.
+ ///
+ /// The stream to import files from. Should be in a supported archive format.
+ /// The filename of the archive being imported.
+ Task Import(Stream stream, string filename);
+
///
/// An array of accepted file extensions (in the standard format of ".abc").
///
diff --git a/osu.Game/Database/ImportTask.cs b/osu.Game/Database/ImportTask.cs
new file mode 100644
index 0000000000..1433a567a9
--- /dev/null
+++ b/osu.Game/Database/ImportTask.cs
@@ -0,0 +1,75 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+#nullable enable
+
+using System.IO;
+using osu.Game.IO.Archives;
+using osu.Game.Utils;
+using SharpCompress.Common;
+
+namespace osu.Game.Database
+{
+ ///
+ /// An encapsulated import task to be imported to an .
+ ///
+ public class ImportTask
+ {
+ ///
+ /// The path to the file (or filename in the case a stream is provided).
+ ///
+ public string Path { get; }
+
+ ///
+ /// An optional stream which provides the file content.
+ ///
+ public Stream? Stream { get; }
+
+ ///
+ /// Construct a new import task from a path (on a local filesystem).
+ ///
+ public ImportTask(string path)
+ {
+ Path = path;
+ }
+
+ ///
+ /// Construct a new import task from a stream.
+ ///
+ public ImportTask(Stream stream, string filename)
+ {
+ Path = filename;
+ Stream = stream;
+ }
+
+ ///
+ /// Retrieve an archive reader from this task.
+ ///
+ public ArchiveReader GetReader()
+ {
+ if (Stream != null)
+ return new ZipArchiveReader(Stream, Path);
+
+ return getReaderFrom(Path);
+ }
+
+ ///
+ /// Creates an from a valid storage path.
+ ///
+ /// A file or folder path resolving the archive content.
+ /// A reader giving access to the archive's content.
+ private ArchiveReader getReaderFrom(string path)
+ {
+ if (ZipUtils.IsZipArchive(path))
+ return new ZipArchiveReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read), System.IO.Path.GetFileName(path));
+ if (Directory.Exists(path))
+ return new LegacyDirectoryArchiveReader(path);
+ if (File.Exists(path))
+ return new LegacyFileArchiveReader(path);
+
+ throw new InvalidFormatException($"{path} is not a valid archive");
+ }
+
+ public override string ToString() => System.IO.Path.GetFileName(Path);
+ }
+}
diff --git a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs b/osu.Game/Graphics/UserInterface/SlimEnumDropdown.cs
similarity index 94%
rename from osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs
rename to osu.Game/Graphics/UserInterface/SlimEnumDropdown.cs
index 9e7ff1205f..965734792c 100644
--- a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/SlimEnumDropdown.cs
@@ -2,14 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
-using osu.Game.Graphics.UserInterface;
using osuTK;
+using osuTK.Graphics;
-namespace osu.Game.Overlays.SearchableList
+namespace osu.Game.Graphics.UserInterface
{
public class SlimEnumDropdown : OsuEnumDropdown
where T : struct, Enum
diff --git a/osu.Game/Online/Multiplayer/GetRoomsRequest.cs b/osu.Game/Online/Multiplayer/GetRoomsRequest.cs
index 64e0386f77..a0609f77dd 100644
--- a/osu.Game/Online/Multiplayer/GetRoomsRequest.cs
+++ b/osu.Game/Online/Multiplayer/GetRoomsRequest.cs
@@ -11,24 +11,24 @@ namespace osu.Game.Online.Multiplayer
{
public class GetRoomsRequest : APIRequest>
{
- private readonly RoomStatusFilter statusFilter;
- private readonly RoomCategoryFilter categoryFilter;
+ private readonly RoomStatusFilter status;
+ private readonly string category;
- public GetRoomsRequest(RoomStatusFilter statusFilter, RoomCategoryFilter categoryFilter)
+ public GetRoomsRequest(RoomStatusFilter status, string category)
{
- this.statusFilter = statusFilter;
- this.categoryFilter = categoryFilter;
+ this.status = status;
+ this.category = category;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
- if (statusFilter != RoomStatusFilter.Open)
- req.AddParameter("mode", statusFilter.ToString().Underscore().ToLowerInvariant());
+ if (status != RoomStatusFilter.Open)
+ req.AddParameter("mode", status.ToString().Underscore().ToLowerInvariant());
- if (categoryFilter != RoomCategoryFilter.Any)
- req.AddParameter("category", categoryFilter.ToString().Underscore().ToLowerInvariant());
+ if (!string.IsNullOrEmpty(category))
+ req.AddParameter("category", category);
return req;
}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index e7b5d3304d..0fc2b8d1d7 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -395,6 +395,17 @@ namespace osu.Game
}
}
+ public async Task Import(Stream stream, string filename)
+ {
+ var extension = Path.GetExtension(filename)?.ToLowerInvariant();
+
+ foreach (var importer in fileImporters)
+ {
+ if (importer.HandledExtensions.Contains(extension))
+ await importer.Import(stream, Path.GetFileNameWithoutExtension(filename));
+ }
+ }
+
public IEnumerable HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions);
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index 31adf47456..34f5c70adb 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -158,37 +158,57 @@ namespace osu.Game.Overlays.Mods
},
new Drawable[]
{
- // Body
- new OsuScrollContainer
+ new Container
{
- ScrollbarVisible = false,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding
+ Children = new Drawable[]
{
- Vertical = 10,
- Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING
- },
- Child = ModSectionsContainer = new FillFlowContainer
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Spacing = new Vector2(0f, 10f),
- Width = content_width,
- LayoutDuration = 200,
- LayoutEasing = Easing.OutQuint,
- Children = new ModSection[]
+ // Body
+ new OsuScrollContainer
{
- new DifficultyReductionSection { Action = modButtonPressed },
- new DifficultyIncreaseSection { Action = modButtonPressed },
- new AutomationSection { Action = modButtonPressed },
- new ConversionSection { Action = modButtonPressed },
- new FunSection { Action = modButtonPressed },
- }
- },
+ ScrollbarVisible = false,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding
+ {
+ Vertical = 10,
+ Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING
+ },
+ Children = new Drawable[]
+ {
+ ModSectionsContainer = new FillFlowContainer
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(0f, 10f),
+ Width = content_width,
+ LayoutDuration = 200,
+ LayoutEasing = Easing.OutQuint,
+ Children = new ModSection[]
+ {
+ new DifficultyReductionSection { Action = modButtonPressed },
+ new DifficultyIncreaseSection { Action = modButtonPressed },
+ new AutomationSection { Action = modButtonPressed },
+ new ConversionSection { Action = modButtonPressed },
+ new FunSection { Action = modButtonPressed },
+ }
+ },
+ }
+ },
+ ModSettingsContainer = new ModSettingsContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.BottomRight,
+ Origin = Anchor.BottomRight,
+ Width = 0.3f,
+ Alpha = 0,
+ Padding = new MarginPadding(30),
+ SelectedMods = { BindTarget = SelectedMods },
+ },
+ }
},
},
new Drawable[]
@@ -237,7 +257,7 @@ namespace osu.Game.Overlays.Mods
{
Width = 180,
Text = "Customisation",
- Action = () => ModSettingsContainer.Alpha = ModSettingsContainer.Alpha == 1 ? 0 : 1,
+ Action = () => ModSettingsContainer.ToggleVisibility(),
Enabled = { Value = false },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
@@ -281,16 +301,6 @@ namespace osu.Game.Overlays.Mods
},
},
},
- ModSettingsContainer = new ModSettingsContainer
- {
- RelativeSizeAxes = Axes.Both,
- Anchor = Anchor.BottomRight,
- Origin = Anchor.BottomRight,
- Width = 0.25f,
- Alpha = 0,
- X = -100,
- SelectedMods = { BindTarget = SelectedMods },
- }
};
((IBindable)CustomiseButton.Enabled).BindTo(ModSettingsContainer.HasSettingsForSelection);
@@ -430,7 +440,7 @@ namespace osu.Game.Overlays.Mods
DeselectTypes(selectedMod.IncompatibleMods, true);
- if (selectedMod.RequiresConfiguration) ModSettingsContainer.Alpha = 1;
+ if (selectedMod.RequiresConfiguration) ModSettingsContainer.Show();
}
else
{
diff --git a/osu.Game/Overlays/Mods/ModSettingsContainer.cs b/osu.Game/Overlays/Mods/ModSettingsContainer.cs
index b185b56ecd..1c57ff54ad 100644
--- a/osu.Game/Overlays/Mods/ModSettingsContainer.cs
+++ b/osu.Game/Overlays/Mods/ModSettingsContainer.cs
@@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Overlays.Mods
{
- public class ModSettingsContainer : Container
+ public class ModSettingsContainer : VisibilityContainer
{
public readonly IBindable> SelectedMods = new Bindable>(Array.Empty());
@@ -27,26 +27,40 @@ namespace osu.Game.Overlays.Mods
private readonly FillFlowContainer modSettingsContent;
+ private readonly Container content;
+
+ private const double transition_duration = 400;
+
public ModSettingsContainer()
{
- Children = new Drawable[]
+ RelativeSizeAxes = Axes.Both;
+
+ Child = content = new Container
{
- new Box
+ Masking = true,
+ CornerRadius = 10,
+ RelativeSizeAxes = Axes.Both,
+ RelativePositionAxes = Axes.Both,
+ X = 1,
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Colour = new Color4(0, 0, 0, 192)
- },
- new OsuScrollContainer
- {
- RelativeSizeAxes = Axes.Both,
- Child = modSettingsContent = new FillFlowContainer
+ new Box
{
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Spacing = new Vector2(0f, 10f),
- Padding = new MarginPadding(20),
+ RelativeSizeAxes = Axes.Both,
+ Colour = new Color4(0, 0, 0, 192)
+ },
+ new OsuScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = modSettingsContent = new FillFlowContainer
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(0f, 10f),
+ Padding = new MarginPadding(20),
+ }
}
}
};
@@ -80,5 +94,17 @@ namespace osu.Game.Overlays.Mods
protected override bool OnMouseDown(MouseDownEvent e) => true;
protected override bool OnHover(HoverEvent e) => true;
+
+ protected override void PopIn()
+ {
+ this.FadeIn(transition_duration, Easing.OutQuint);
+ content.MoveToX(0, transition_duration, Easing.OutQuint);
+ }
+
+ protected override void PopOut()
+ {
+ this.FadeOut(transition_duration, Easing.OutQuint);
+ content.MoveToX(1, transition_duration, Easing.OutQuint);
+ }
}
}
diff --git a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs
deleted file mode 100644
index ffbc1c9586..0000000000
--- a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs
+++ /dev/null
@@ -1,84 +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 osu.Framework.Bindables;
-using osuTK;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
-using osu.Game.Graphics.Containers;
-
-namespace osu.Game.Overlays.SearchableList
-{
- public class DisplayStyleControl : CompositeDrawable
- {
- public readonly Bindable DisplayStyle = new Bindable();
-
- public DisplayStyleControl()
- {
- AutoSizeAxes = Axes.Both;
-
- InternalChild = new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Spacing = new Vector2(5f, 0f),
- Direction = FillDirection.Horizontal,
- Children = new[]
- {
- new DisplayStyleToggleButton(FontAwesome.Solid.ThLarge, PanelDisplayStyle.Grid, DisplayStyle),
- new DisplayStyleToggleButton(FontAwesome.Solid.ListUl, PanelDisplayStyle.List, DisplayStyle),
- },
- };
-
- DisplayStyle.Value = PanelDisplayStyle.Grid;
- }
-
- private class DisplayStyleToggleButton : OsuClickableContainer
- {
- private readonly SpriteIcon icon;
- private readonly PanelDisplayStyle style;
- private readonly Bindable bindable;
-
- public DisplayStyleToggleButton(IconUsage icon, PanelDisplayStyle style, Bindable bindable)
- {
- this.bindable = bindable;
- this.style = style;
- Size = new Vector2(25f);
-
- Children = new Drawable[]
- {
- this.icon = new SpriteIcon
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Icon = icon,
- Size = new Vector2(18),
- Alpha = 0.5f,
- },
- };
-
- bindable.ValueChanged += Bindable_ValueChanged;
- Bindable_ValueChanged(new ValueChangedEvent(bindable.Value, bindable.Value));
- Action = () => bindable.Value = this.style;
- }
-
- private void Bindable_ValueChanged(ValueChangedEvent e)
- {
- icon.FadeTo(e.NewValue == style ? 1.0f : 0.5f, 100);
- }
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
-
- bindable.ValueChanged -= Bindable_ValueChanged;
- }
- }
- }
-
- public enum PanelDisplayStyle
- {
- Grid,
- List,
- }
-}
diff --git a/osu.Game/Overlays/SearchableList/HeaderTabControl.cs b/osu.Game/Overlays/SearchableList/HeaderTabControl.cs
deleted file mode 100644
index 2087a72c54..0000000000
--- a/osu.Game/Overlays/SearchableList/HeaderTabControl.cs
+++ /dev/null
@@ -1,29 +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 osuTK.Graphics;
-using osu.Framework.Graphics.UserInterface;
-using osu.Game.Graphics.UserInterface;
-
-namespace osu.Game.Overlays.SearchableList
-{
- public class HeaderTabControl : OsuTabControl
- {
- protected override TabItem CreateTabItem(T value) => new HeaderTabItem(value);
-
- public HeaderTabControl()
- {
- Height = 26;
- AccentColour = Color4.White;
- }
-
- private class HeaderTabItem : OsuTabItem
- {
- public HeaderTabItem(T value)
- : base(value)
- {
- Text.Font = Text.Font.With(size: 16);
- }
- }
- }
-}
diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
deleted file mode 100644
index 1990674aa9..0000000000
--- a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
+++ /dev/null
@@ -1,165 +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;
-using osuTK.Graphics;
-using osu.Framework.Allocation;
-using osu.Framework.Extensions.Color4Extensions;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Game.Graphics;
-using osu.Game.Graphics.UserInterface;
-using osu.Framework.Graphics.Shapes;
-
-namespace osu.Game.Overlays.SearchableList
-{
- public abstract class SearchableListFilterControl : Container
- where TTab : struct, Enum
- where TCategory : struct, Enum
- {
- private const float padding = 10;
-
- private readonly Drawable filterContainer;
- private readonly Drawable rightFilterContainer;
- private readonly Box tabStrip;
-
- public readonly SearchTextBox Search;
- public readonly PageTabControl Tabs;
- public readonly SlimEnumDropdown Dropdown;
- public readonly DisplayStyleControl DisplayStyleControl;
-
- protected abstract Color4 BackgroundColour { get; }
- protected abstract TTab DefaultTab { get; }
- protected abstract TCategory DefaultCategory { get; }
- protected virtual Drawable CreateSupplementaryControls() => null;
-
- ///
- /// The amount of padding added to content (does not affect background or tab control strip).
- ///
- protected virtual float ContentHorizontalPadding => WaveOverlayContainer.WIDTH_PADDING;
-
- protected SearchableListFilterControl()
- {
- RelativeSizeAxes = Axes.X;
-
- var controls = CreateSupplementaryControls();
- Container controlsContainer;
- Children = new[]
- {
- filterContainer = new Container
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = BackgroundColour,
- Alpha = 0.9f,
- },
- tabStrip = new Box
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- RelativeSizeAxes = Axes.X,
- Height = 1,
- },
- new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Padding = new MarginPadding
- {
- Top = padding,
- Horizontal = ContentHorizontalPadding
- },
- Children = new Drawable[]
- {
- Search = new FilterSearchTextBox
- {
- RelativeSizeAxes = Axes.X,
- },
- controlsContainer = new Container
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Margin = new MarginPadding { Top = controls != null ? padding : 0 },
- },
- new Container
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Padding = new MarginPadding { Right = 225 },
- Child = Tabs = new PageTabControl
- {
- RelativeSizeAxes = Axes.X,
- },
- },
- new Box // keep the tab strip part of autosize, but don't put it in the flow container
- {
- RelativeSizeAxes = Axes.X,
- Height = 1,
- Colour = Color4.White.Opacity(0),
- },
- },
- },
- },
- },
- rightFilterContainer = new FillFlowContainer
- {
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- AutoSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- Dropdown = new SlimEnumDropdown
- {
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- RelativeSizeAxes = Axes.None,
- Width = 160f,
- },
- DisplayStyleControl = new DisplayStyleControl
- {
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- },
- }
- }
- };
-
- if (controls != null) controlsContainer.Children = new[] { controls };
-
- Tabs.Current.Value = DefaultTab;
- Tabs.Current.TriggerChange();
-
- Dropdown.Current.Value = DefaultCategory;
- Dropdown.Current.TriggerChange();
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- tabStrip.Colour = colours.Yellow;
- }
-
- protected override void Update()
- {
- base.Update();
-
- Height = filterContainer.Height;
- rightFilterContainer.Margin = new MarginPadding { Top = filterContainer.Height - 30, Right = ContentHorizontalPadding };
- }
-
- private class FilterSearchTextBox : SearchTextBox
- {
- [BackgroundDependencyLoader]
- private void load()
- {
- BackgroundUnfocused = OsuColour.Gray(0.06f);
- BackgroundFocused = OsuColour.Gray(0.12f);
- }
- }
- }
-}
diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
index b54ad9a641..455e13711d 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
@@ -20,6 +20,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input
private Bindable sensitivityBindable = new BindableDouble();
private Bindable ignoredInputHandlers;
+ private Bindable windowMode;
+ private SettingsEnumDropdown confineMouseModeSetting;
+
[BackgroundDependencyLoader]
private void load(OsuConfigManager osuConfig, FrameworkConfigManager config)
{
@@ -47,7 +50,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
LabelText = "Map absolute input to window",
Current = config.GetBindable(FrameworkSetting.MapAbsoluteInputToWindow)
},
- new SettingsEnumDropdown
+ confineMouseModeSetting = new SettingsEnumDropdown
{
LabelText = "Confine mouse cursor to window",
Current = osuConfig.GetBindable(OsuSetting.ConfineMouseMode)
@@ -64,6 +67,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input
},
};
+ windowMode = config.GetBindable(FrameworkSetting.WindowMode);
+ windowMode.BindValueChanged(mode => confineMouseModeSetting.Alpha = mode.NewValue == WindowMode.Fullscreen ? 0 : 1, true);
+
if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows)
{
rawInputToggle.Disabled = true;
diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
index 3a5e3c098f..289578f3d8 100644
--- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
@@ -225,10 +225,19 @@ namespace osu.Game.Rulesets.UI.Scrolling
hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject);
toComputeLifetime.Clear();
+ }
- // only AliveObjects need to be considered for layout (reduces overhead in the case of scroll speed changes).
+ protected override void UpdateAfterChildrenLife()
+ {
+ base.UpdateAfterChildrenLife();
+
+ // We need to calculate hit object positions (including nested hit objects) as soon as possible after lifetimes
+ // to prevent hit objects displayed in a wrong position for one frame.
+ // Only AliveObjects need to be considered for layout (reduces overhead in the case of scroll speed changes).
foreach (var obj in AliveObjects)
{
+ updatePosition(obj, Time.Current);
+
if (layoutComputed.Contains(obj))
continue;
@@ -293,15 +302,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
}
}
- protected override void UpdateAfterChildrenLife()
- {
- base.UpdateAfterChildrenLife();
-
- // We need to calculate hitobject positions as soon as possible after lifetimes so that hitobjects get the final say in their positions
- foreach (var obj in AliveObjects)
- updatePosition(obj, Time.Current);
- }
-
private void updatePosition(DrawableHitObject hitObject, double currentTime)
{
switch (direction.Value)
diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs
index 17ecfdd52e..0c957b80af 100644
--- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs
+++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs
@@ -1,6 +1,7 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
@@ -99,6 +100,8 @@ namespace osu.Game.Screens.Edit.Setup
return Task.CompletedTask;
}
+ Task ICanAcceptFiles.Import(Stream stream, string filename) => throw new NotImplementedException();
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs
index 3fc1359006..896c215c42 100644
--- a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs
+++ b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs
@@ -1,24 +1,23 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
using osu.Framework.Threading;
-using osu.Game.Overlays.SearchableList;
+using osu.Game.Graphics;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osuTK.Graphics;
namespace osu.Game.Screens.Multi.Lounge.Components
{
- public class FilterControl : SearchableListFilterControl
+ public abstract class FilterControl : CompositeDrawable
{
- protected override Color4 BackgroundColour => Color4.Black.Opacity(0.5f);
- protected override RoomStatusFilter DefaultTab => RoomStatusFilter.Open;
- protected override RoomCategoryFilter DefaultCategory => RoomCategoryFilter.Any;
-
- protected override float ContentHorizontalPadding => base.ContentHorizontalPadding + OsuScreen.HORIZONTAL_OVERFLOW_PADDING;
+ protected const float VERTICAL_PADDING = 10;
+ protected const float HORIZONTAL_PADDING = 80;
[Resolved(CanBeNull = true)]
private Bindable filter { get; set; }
@@ -26,66 +25,109 @@ namespace osu.Game.Screens.Multi.Lounge.Components
[Resolved]
private IBindable ruleset { get; set; }
- public FilterControl()
+ private readonly Box tabStrip;
+ private readonly SearchTextBox search;
+ private readonly PageTabControl tabs;
+
+ protected FilterControl()
{
- DisplayStyleControl.Hide();
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ Alpha = 0.25f,
+ },
+ tabStrip = new Box
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = 1,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding
+ {
+ Top = VERTICAL_PADDING,
+ Horizontal = HORIZONTAL_PADDING
+ },
+ Children = new Drawable[]
+ {
+ search = new FilterSearchTextBox
+ {
+ RelativeSizeAxes = Axes.X,
+ },
+ tabs = new PageTabControl
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ },
+ }
+ }
+ };
+
+ tabs.Current.Value = RoomStatusFilter.Open;
+ tabs.Current.TriggerChange();
}
[BackgroundDependencyLoader]
- private void load()
+ private void load(OsuColour colours)
{
filter ??= new Bindable();
+ tabStrip.Colour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
- ruleset.BindValueChanged(_ => updateFilter());
- Search.Current.BindValueChanged(_ => scheduleUpdateFilter());
- Dropdown.Current.BindValueChanged(_ => updateFilter());
- Tabs.Current.BindValueChanged(_ => updateFilter(), true);
+ search.Current.BindValueChanged(_ => updateFilterDebounced());
+ ruleset.BindValueChanged(_ => UpdateFilter());
+ tabs.Current.BindValueChanged(_ => UpdateFilter(), true);
}
private ScheduledDelegate scheduledFilterUpdate;
- private void scheduleUpdateFilter()
+ private void updateFilterDebounced()
{
scheduledFilterUpdate?.Cancel();
- scheduledFilterUpdate = Scheduler.AddDelayed(updateFilter, 200);
+ scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200);
}
- private void updateFilter()
+ protected void UpdateFilter()
{
scheduledFilterUpdate?.Cancel();
- if (filter == null)
- return;
+ var criteria = CreateCriteria();
+ criteria.SearchString = search.Current.Value;
+ criteria.Status = tabs.Current.Value;
+ criteria.Ruleset = ruleset.Value;
- filter.Value = new FilterCriteria
+ filter.Value = criteria;
+ }
+
+ protected virtual FilterCriteria CreateCriteria() => new FilterCriteria();
+
+ public bool HoldFocus
+ {
+ get => search.HoldFocus;
+ set => search.HoldFocus = value;
+ }
+
+ public void TakeFocus() => search.TakeFocus();
+
+ private class FilterSearchTextBox : SearchTextBox
+ {
+ [BackgroundDependencyLoader]
+ private void load()
{
- SearchString = Search.Current.Value ?? string.Empty,
- StatusFilter = Tabs.Current.Value,
- RoomCategoryFilter = Dropdown.Current.Value,
- Ruleset = ruleset.Value
- };
+ BackgroundUnfocused = OsuColour.Gray(0.06f);
+ BackgroundFocused = OsuColour.Gray(0.12f);
+ }
}
}
-
- public enum RoomStatusFilter
- {
- Open,
-
- [Description("Recently Ended")]
- Ended,
- Participated,
- Owned,
- }
-
- public enum RoomCategoryFilter
- {
- Any,
- Normal,
- Spotlight
- }
}
diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterCriteria.cs b/osu.Game/Screens/Multi/Lounge/Components/FilterCriteria.cs
index 6d70225eec..7b04be86b1 100644
--- a/osu.Game/Screens/Multi/Lounge/Components/FilterCriteria.cs
+++ b/osu.Game/Screens/Multi/Lounge/Components/FilterCriteria.cs
@@ -8,8 +8,8 @@ namespace osu.Game.Screens.Multi.Lounge.Components
public class FilterCriteria
{
public string SearchString;
- public RoomStatusFilter StatusFilter;
- public RoomCategoryFilter RoomCategoryFilter;
+ public RoomStatusFilter Status;
+ public string Category;
public RulesetInfo Ruleset;
}
}
diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomStatusFilter.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomStatusFilter.cs
new file mode 100644
index 0000000000..9da938ac8b
--- /dev/null
+++ b/osu.Game/Screens/Multi/Lounge/Components/RoomStatusFilter.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 System.ComponentModel;
+
+namespace osu.Game.Screens.Multi.Lounge.Components
+{
+ public enum RoomStatusFilter
+ {
+ Open,
+
+ [Description("Recently Ended")]
+ Ended,
+ Participated,
+ Owned,
+ }
+}
diff --git a/osu.Game/Screens/Multi/Lounge/Components/TimeshiftFilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/TimeshiftFilterControl.cs
new file mode 100644
index 0000000000..68cab283a0
--- /dev/null
+++ b/osu.Game/Screens/Multi/Lounge/Components/TimeshiftFilterControl.cs
@@ -0,0 +1,59 @@
+// 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.UserInterface;
+using osu.Game.Graphics.UserInterface;
+
+namespace osu.Game.Screens.Multi.Lounge.Components
+{
+ public class TimeshiftFilterControl : FilterControl
+ {
+ private readonly Dropdown dropdown;
+
+ public TimeshiftFilterControl()
+ {
+ AddInternal(dropdown = new SlimEnumDropdown
+ {
+ Anchor = Anchor.BottomRight,
+ Origin = Anchor.TopRight,
+ RelativeSizeAxes = Axes.None,
+ Width = 160,
+ X = -HORIZONTAL_PADDING,
+ Y = -30
+ });
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ dropdown.Current.BindValueChanged(_ => UpdateFilter());
+ }
+
+ protected override FilterCriteria CreateCriteria()
+ {
+ var criteria = base.CreateCriteria();
+
+ switch (dropdown.Current.Value)
+ {
+ case TimeshiftCategory.Normal:
+ criteria.Category = "normal";
+ break;
+
+ case TimeshiftCategory.Spotlight:
+ criteria.Category = "spotlight";
+ break;
+ }
+
+ return criteria;
+ }
+
+ private enum TimeshiftCategory
+ {
+ Any,
+ Normal,
+ Spotlight
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs
index 4dc9ba549b..a26a64d86d 100644
--- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs
@@ -48,7 +48,6 @@ namespace osu.Game.Screens.Multi.Lounge
InternalChildren = new Drawable[]
{
- Filter = new FilterControl { Depth = -1 },
content = new Container
{
RelativeSizeAxes = Axes.Both,
@@ -79,6 +78,11 @@ namespace osu.Game.Screens.Multi.Lounge
},
},
},
+ Filter = new TimeshiftFilterControl
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 80,
+ },
};
// scroll selected room into view on selection.
@@ -112,7 +116,7 @@ namespace osu.Game.Screens.Multi.Lounge
protected override void OnFocus(FocusEvent e)
{
- Filter.Search.TakeFocus();
+ Filter.TakeFocus();
}
public override void OnEntering(IScreen last)
@@ -136,19 +140,19 @@ namespace osu.Game.Screens.Multi.Lounge
private void onReturning()
{
- Filter.Search.HoldFocus = true;
+ Filter.HoldFocus = true;
}
public override bool OnExiting(IScreen next)
{
- Filter.Search.HoldFocus = false;
+ Filter.HoldFocus = false;
return base.OnExiting(next);
}
public override void OnSuspending(IScreen next)
{
base.OnSuspending(next);
- Filter.Search.HoldFocus = false;
+ Filter.HoldFocus = false;
}
private void joinRequested(Room room)
diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs
index 668a373d80..b8003b9774 100644
--- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs
+++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs
@@ -135,6 +135,7 @@ namespace osu.Game.Screens.Multi.Match.Components
{
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this,
+ LengthLimit = 100
},
},
new Section("Duration")
diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs
index 2a96fa536d..fb0cf73bb9 100644
--- a/osu.Game/Screens/Multi/RoomManager.cs
+++ b/osu.Game/Screens/Multi/RoomManager.cs
@@ -317,7 +317,7 @@ namespace osu.Game.Screens.Multi
var tcs = new TaskCompletionSource();
pollReq?.Cancel();
- pollReq = new GetRoomsRequest(currentFilter.Value.StatusFilter, currentFilter.Value.RoomCategoryFilter);
+ pollReq = new GetRoomsRequest(currentFilter.Value.Status, currentFilter.Value.Category);
pollReq.Success += result =>
{
diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs
index 2c83161614..0248432917 100644
--- a/osu.Game/Screens/Play/GameplayClockContainer.cs
+++ b/osu.Game/Screens/Play/GameplayClockContainer.cs
@@ -95,6 +95,16 @@ namespace osu.Game.Screens.Play
localGameplayClock = new LocalGameplayClock(userOffsetClock);
GameplayClock.IsPaused.BindTo(IsPaused);
+
+ IsPaused.BindValueChanged(onPauseChanged);
+ }
+
+ private void onPauseChanged(ValueChangedEvent isPaused)
+ {
+ if (isPaused.NewValue)
+ this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => adjustableClock.Stop());
+ else
+ this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In);
}
private double totalOffset => userOffsetClock.Offset + platformOffsetClock.Offset;
@@ -154,13 +164,16 @@ namespace osu.Game.Screens.Play
public void Start()
{
- // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time
- // This accounts for the audio clock source potentially taking time to enter a completely stopped state
- Seek(GameplayClock.CurrentTime);
- adjustableClock.Start();
- IsPaused.Value = false;
+ if (!adjustableClock.IsRunning)
+ {
+ // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time
+ // This accounts for the audio clock source potentially taking time to enter a completely stopped state
+ Seek(GameplayClock.CurrentTime);
- this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In);
+ adjustableClock.Start();
+ }
+
+ IsPaused.Value = false;
}
///
@@ -199,8 +212,6 @@ namespace osu.Game.Screens.Play
public void Stop()
{
- this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => adjustableClock.Stop());
-
IsPaused.Value = true;
}
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index 42074ac241..729119fa36 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -71,8 +72,9 @@ namespace osu.Game.Screens.Play
}
private bool readyForPush =>
+ !playerConsumed
// don't push unless the player is completely loaded
- player?.LoadState == LoadState.Ready
+ && player?.LoadState == LoadState.Ready
// don't push if the user is hovering one of the panes, unless they are idle.
&& (IsHovered || idleTracker.IsIdle.Value)
// don't push if the user is dragging a slider or otherwise.
@@ -84,6 +86,11 @@ namespace osu.Game.Screens.Play
private Player player;
+ ///
+ /// Whether the curent player instance has been consumed via .
+ ///
+ private bool playerConsumed;
+
private LogoTrackingContainer content;
private bool hideOverlays;
@@ -179,7 +186,10 @@ namespace osu.Game.Screens.Play
contentIn();
MetadataInfo.Delay(750).FadeIn(500);
- this.Delay(1800).Schedule(pushWhenLoaded);
+
+ // after an initial delay, start the debounced load check.
+ // this will continue to execute even after resuming back on restart.
+ Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, 1800, 0));
showMuteWarningIfNeeded();
}
@@ -188,17 +198,18 @@ namespace osu.Game.Screens.Play
{
base.OnResuming(last);
- contentIn();
+ // prepare for a retry.
+ player = null;
+ playerConsumed = false;
+ cancelLoad();
- this.Delay(400).Schedule(pushWhenLoaded);
+ contentIn();
}
public override void OnSuspending(IScreen next)
{
base.OnSuspending(next);
- cancelLoad();
-
BackgroundBrightnessReduction = false;
// we're moving to player, so a period of silence is upcoming.
@@ -274,6 +285,14 @@ namespace osu.Game.Screens.Play
}
}
+ private Player consumePlayer()
+ {
+ Debug.Assert(!playerConsumed);
+
+ playerConsumed = true;
+ return player;
+ }
+
private void prepareNewPlayer()
{
if (!this.IsCurrentScreen())
@@ -315,64 +334,62 @@ namespace osu.Game.Screens.Play
{
if (!this.IsCurrentScreen()) return;
- try
+ if (!readyForPush)
{
- if (!readyForPush)
+ // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
+ // if we become unready for push during the delay.
+ cancelLoad();
+ return;
+ }
+
+ // if a push has already been scheduled, no further action is required.
+ // this value is reset via cancelLoad() to allow a second usage of the same PlayerLoader screen.
+ if (scheduledPushPlayer != null)
+ return;
+
+ scheduledPushPlayer = Scheduler.AddDelayed(() =>
+ {
+ // ensure that once we have reached this "point of no return", readyForPush will be false for all future checks (until a new player instance is prepared).
+ var consumedPlayer = consumePlayer();
+
+ contentOut();
+
+ TransformSequence pushSequence = this.Delay(250);
+
+ // only show if the warning was created (i.e. the beatmap needs it)
+ // and this is not a restart of the map (the warning expires after first load).
+ if (epilepsyWarning?.IsAlive == true)
{
- // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
- // if we become unready for push during the delay.
- cancelLoad();
- return;
+ const double epilepsy_display_length = 3000;
+
+ pushSequence
+ .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible)
+ .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint)
+ .Delay(epilepsy_display_length)
+ .Schedule(() =>
+ {
+ epilepsyWarning.Hide();
+ epilepsyWarning.Expire();
+ })
+ .Delay(EpilepsyWarning.FADE_DURATION);
}
- if (scheduledPushPlayer != null)
- return;
-
- scheduledPushPlayer = Scheduler.AddDelayed(() =>
+ pushSequence.Schedule(() =>
{
- contentOut();
+ if (!this.IsCurrentScreen()) return;
- TransformSequence pushSequence = this.Delay(250);
+ LoadTask = null;
- // only show if the warning was created (i.e. the beatmap needs it)
- // and this is not a restart of the map (the warning expires after first load).
- if (epilepsyWarning?.IsAlive == true)
- {
- const double epilepsy_display_length = 3000;
+ // By default, we want to load the player and never be returned to.
+ // Note that this may change if the player we load requested a re-run.
+ ValidForResume = false;
- pushSequence
- .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible)
- .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint)
- .Delay(epilepsy_display_length)
- .Schedule(() =>
- {
- epilepsyWarning.Hide();
- epilepsyWarning.Expire();
- })
- .Delay(EpilepsyWarning.FADE_DURATION);
- }
-
- pushSequence.Schedule(() =>
- {
- if (!this.IsCurrentScreen()) return;
-
- LoadTask = null;
-
- // By default, we want to load the player and never be returned to.
- // Note that this may change if the player we load requested a re-run.
- ValidForResume = false;
-
- if (player.LoadedBeatmapSuccessfully)
- this.Push(player);
- else
- this.Exit();
- });
- }, 500);
- }
- finally
- {
- Schedule(pushWhenLoaded);
- }
+ if (consumedPlayer.LoadedBeatmapSuccessfully)
+ this.Push(consumedPlayer);
+ else
+ this.Exit();
+ });
+ }, 500);
}
private void cancelLoad()
@@ -390,7 +407,7 @@ namespace osu.Game.Screens.Play
if (isDisposing)
{
// if the player never got pushed, we should explicitly dispose it.
- DisposalTask = LoadTask?.ContinueWith(_ => player.Dispose());
+ DisposalTask = LoadTask?.ContinueWith(_ => player?.Dispose());
}
}
diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs
index 19b96d6c60..cc6b85a13e 100644
--- a/osu.Game/Skinning/PoolableSkinnableSample.cs
+++ b/osu.Game/Skinning/PoolableSkinnableSample.cs
@@ -102,7 +102,7 @@ namespace osu.Game.Skinning
sampleContainer.Add(Sample = new DrawableSample(ch) { Looping = Looping });
// Start playback internally for the new sample if the previous one was playing beforehand.
- if (wasPlaying)
+ if (wasPlaying && Looping)
Play();
}
diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs
index 23159e4fe1..645c08cd00 100644
--- a/osu.Game/Skinning/SkinnableSound.cs
+++ b/osu.Game/Skinning/SkinnableSound.cs
@@ -159,7 +159,7 @@ namespace osu.Game.Skinning
samplesContainer.Add(sample);
}
- if (wasPlaying)
+ if (wasPlaying && Looping)
Play();
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 9d37ceee6c..b4c7dca12f 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index ab03393836..7542aded86 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -88,7 +88,7 @@
-
+