diff --git a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml
index 7515e76054..4bb9f4d2a0 100644
--- a/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml
+++ b/.idea/.idea.osu.Desktop/.idea/projectSettingsUpdater.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/global.json b/global.json
index 6858d4044d..6c793a3f1d 100644
--- a/global.json
+++ b/global.json
@@ -5,6 +5,6 @@
"version": "3.1.100"
},
"msbuild-sdks": {
- "Microsoft.Build.Traversal": "2.0.24"
+ "Microsoft.Build.Traversal": "2.0.34"
}
}
\ No newline at end of file
diff --git a/osu.Android.props b/osu.Android.props
index b147fdd05b..aaac6ec427 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,7 +51,7 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs b/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs
new file mode 100644
index 0000000000..0c46b078b5
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/CatchSkinnableTestScene.cs
@@ -0,0 +1,21 @@
+// 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 osu.Game.Rulesets.Catch.Skinning;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ public abstract class CatchSkinnableTestScene : SkinnableTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(CatchRuleset),
+ typeof(CatchLegacySkinTransformer),
+ };
+
+ protected override Ruleset CreateRulesetForSkinProvider() => new CatchRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-fail.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-fail@2x.png
similarity index 100%
rename from osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-fail.png
rename to osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-fail@2x.png
diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-kiai.png b/osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-kiai@2x.png
similarity index 100%
rename from osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-kiai.png
rename to osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-kiai@2x.png
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
index fe0d512166..acc5f4e428 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -4,21 +4,21 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Catch.UI;
-using osu.Game.Tests.Visual;
using System;
using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
- public class TestSceneCatcher : SkinnableTestScene
+ public class TestSceneCatcher : CatchSkinnableTestScene
{
- public override IReadOnlyList RequiredTypes => new[]
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[]
{
typeof(CatcherArea),
typeof(CatcherSprite)
- };
+ }).ToList();
[BackgroundDependencyLoader]
private void load()
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
index cf68c5424d..2b30edb70b 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
@@ -17,12 +17,11 @@ using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
-using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
- public class TestSceneCatcherArea : SkinnableTestScene
+ public class TestSceneCatcherArea : CatchSkinnableTestScene
{
private RulesetInfo catchRuleset;
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs
index 82d5aa936f..cd674bb754 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs
@@ -3,20 +3,20 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
-using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
- public class TestSceneFruitObjects : SkinnableTestScene
+ public class TestSceneFruitObjects : CatchSkinnableTestScene
{
- public override IReadOnlyList RequiredTypes => new[]
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[]
{
typeof(CatchHitObject),
typeof(Fruit),
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Tests
typeof(DrawableBanana),
typeof(DrawableBananaShower),
typeof(Pulp),
- };
+ }).ToList();
protected override void LoadComplete()
{
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index 212365caad..ca75a816f1 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Catch
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
- public override ISkin CreateLegacySkinProvider(ISkinSource source) => new CatchLegacySkinTransformer(source);
+ public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new CatchLegacySkinTransformer(source);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score);
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
index 5880a227c2..4d9dbbbc5f 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
@@ -72,10 +72,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
protected override Skill[] CreateSkills(IBeatmap beatmap)
{
using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty))
- {
halfCatcherWidth = catcher.CatchWidth * 0.5f;
- halfCatcherWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay.
- }
return new Skill[]
{
diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs
index 8fa9c61b6f..7c815370c8 100644
--- a/osu.Game.Rulesets.Catch/UI/Catcher.cs
+++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs
@@ -37,10 +37,15 @@ namespace osu.Game.Rulesets.Catch.UI
public CatcherAnimationState CurrentState { get; private set; }
+ ///
+ /// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable.
+ ///
+ private const float allowed_catch_range = 0.8f;
+
///
/// Width of the area that can be used to attempt catches during gameplay.
///
- internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X);
+ internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X) * allowed_catch_range;
protected bool Dashing
{
@@ -379,7 +384,7 @@ namespace osu.Game.Rulesets.Catch.UI
}
currentCatcher.Show();
- (currentCatcher.Drawable as IAnimation)?.GotoFrame(0);
+ (currentCatcher.Drawable as IFramedAnimation)?.GotoFrame(0);
}
private void beginTrail()
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
index 52eb8d597e..ef69e3d2d1 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.UI
public CatcherSprite(CatcherAnimationState state)
: base(new CatchSkinComponent(componentFromState(state)), _ =>
- new DefaultCatcherSprite(state), confineMode: ConfineMode.ScaleDownToFit)
+ new DefaultCatcherSprite(state), confineMode: ConfineMode.ScaleToFit)
{
RelativeSizeAxes = Axes.None;
Size = new Vector2(CatcherArea.CATCHER_SIZE);
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs
new file mode 100644
index 0000000000..40a6e1fdae
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs
@@ -0,0 +1,50 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Game.Rulesets.Mania.Beatmaps;
+using NUnit.Framework;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ [TestFixture]
+ public class ManiaColumnTypeTest
+ {
+ [TestCase(new[]
+ {
+ ColumnType.Special
+ }, 1)]
+ [TestCase(new[]
+ {
+ ColumnType.Odd,
+ ColumnType.Even,
+ ColumnType.Even,
+ ColumnType.Odd
+ }, 4)]
+ [TestCase(new[]
+ {
+ ColumnType.Odd,
+ ColumnType.Even,
+ ColumnType.Odd,
+ ColumnType.Special,
+ ColumnType.Odd,
+ ColumnType.Even,
+ ColumnType.Odd
+ }, 7)]
+ public void Test(IEnumerable expected, int columns)
+ {
+ var definition = new StageDefinition
+ {
+ Columns = columns
+ };
+ var results = getResults(definition);
+ Assert.AreEqual(expected, results);
+ }
+
+ private IEnumerable getResults(StageDefinition definition)
+ {
+ for (var i = 0; i < definition.Columns; i++)
+ yield return definition.GetTypeOfColumn(i);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-key1@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-key1@2x.png
new file mode 100644
index 0000000000..aa681f6f22
Binary files /dev/null and b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-key1@2x.png differ
diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-stage-bottom@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-stage-bottom@2x.png
new file mode 100644
index 0000000000..ca590eaf08
Binary files /dev/null and b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-stage-bottom@2x.png differ
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs
new file mode 100644
index 0000000000..ff4865c71d
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs
@@ -0,0 +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.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.UI;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ ///
+ /// A container to be used in a to provide a resolvable dependency.
+ ///
+ public class ColumnTestContainer : Container
+ {
+ protected override Container Content => content;
+
+ private readonly Container content;
+
+ [Cached]
+ private readonly Column column;
+
+ public ColumnTestContainer(int column, ManiaAction action)
+ {
+ this.column = new Column(column)
+ {
+ Action = { Value = action },
+ AccentColour = Color4.Orange,
+ ColumnType = column % 2 == 0 ? ColumnType.Even : ColumnType.Odd
+ };
+
+ InternalChild = content = new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4)
+ {
+ RelativeSizeAxes = Axes.Both
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs
new file mode 100644
index 0000000000..18eebada00
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs
@@ -0,0 +1,72 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.UI.Scrolling;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ ///
+ /// A test scene for a mania hitobject.
+ ///
+ public abstract class ManiaHitObjectTestScene : ManiaSkinnableTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ SetContents(() => new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Height = 0.7f,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ new ColumnTestContainer(0, ManiaAction.Key1)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Y,
+ Width = 80,
+ Child = new ScrollingHitObjectContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ }.With(c =>
+ {
+ c.Add(CreateHitObject().With(h =>
+ {
+ h.HitObject.StartTime = START_TIME;
+ h.AccentColour.Value = Color4.Orange;
+ }));
+ })
+ },
+ new ColumnTestContainer(1, ManiaAction.Key2)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Y,
+ Width = 80,
+ Child = new ScrollingHitObjectContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ }.With(c =>
+ {
+ c.Add(CreateHitObject().With(h =>
+ {
+ h.HitObject.StartTime = START_TIME;
+ h.AccentColour.Value = Color4.Orange;
+ }));
+ })
+ },
+ }
+ });
+ }
+
+ protected abstract DrawableManiaHitObject CreateHitObject();
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs
new file mode 100644
index 0000000000..a3c1d518c5
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs
@@ -0,0 +1,91 @@
+// 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 NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Mania.Skinning;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Rulesets.UI.Scrolling.Algorithms;
+using osu.Game.Tests.Visual;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ ///
+ /// A test scene for skinnable mania components.
+ ///
+ public abstract class ManiaSkinnableTestScene : SkinnableTestScene
+ {
+ protected const double START_TIME = 1000000000;
+
+ [Cached(Type = typeof(IScrollingInfo))]
+ private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ManiaRuleset),
+ typeof(ManiaLegacySkinTransformer),
+ typeof(ManiaSettingsSubsection)
+ };
+
+ protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset();
+
+ protected ManiaSkinnableTestScene()
+ {
+ scrollingInfo.Direction.Value = ScrollingDirection.Down;
+
+ Add(new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.SlateGray.Opacity(0.2f),
+ Depth = 1
+ });
+ }
+
+ [Test]
+ public void TestScrollingDown()
+ {
+ AddStep("change direction to down", () => scrollingInfo.Direction.Value = ScrollingDirection.Down);
+ }
+
+ [Test]
+ public void TestScrollingUp()
+ {
+ AddStep("change direction to up", () => scrollingInfo.Direction.Value = ScrollingDirection.Up);
+ }
+
+ private class TestScrollingInfo : IScrollingInfo
+ {
+ public readonly Bindable Direction = new Bindable();
+
+ IBindable IScrollingInfo.Direction => Direction;
+ IBindable IScrollingInfo.TimeRange { get; } = new Bindable(1000);
+ IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ZeroScrollAlgorithm();
+ }
+
+ private class ZeroScrollAlgorithm : IScrollAlgorithm
+ {
+ public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength)
+ => double.MinValue;
+
+ public float GetLength(double startTime, double endTime, double timeRange, float scrollLength)
+ => scrollLength;
+
+ public float PositionAt(double time, double currentTime, double timeRange, float scrollLength)
+ => (float)((time - START_TIME) / timeRange) * scrollLength;
+
+ public double TimeAt(float position, double currentTime, double timeRange, float scrollLength)
+ => 0;
+
+ public void Reset()
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs
new file mode 100644
index 0000000000..d6bacbe59e
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs
@@ -0,0 +1,49 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Mania.UI.Components;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ public class TestSceneColumnBackground : ManiaSkinnableTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ SetContents(() => new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(0.8f),
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ new ColumnTestContainer(0, ManiaAction.Key1)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.5f,
+ Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 0), _ => new DefaultColumnBackground())
+ {
+ RelativeSizeAxes = Axes.Both
+ }
+ },
+ new ColumnTestContainer(1, ManiaAction.Key2)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.5f,
+ Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 0), _ => new DefaultColumnBackground())
+ {
+ RelativeSizeAxes = Axes.Both
+ }
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs
new file mode 100644
index 0000000000..4392666cb7
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs
@@ -0,0 +1,49 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Mania.UI.Components;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ public class TestSceneColumnHitObjectArea : ManiaSkinnableTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ SetContents(() => new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(0.8f),
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ new ColumnTestContainer(0, ManiaAction.Key1)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.5f,
+ Child = new ColumnHitObjectArea(0, new HitObjectContainer())
+ {
+ RelativeSizeAxes = Axes.Both
+ }
+ },
+ new ColumnTestContainer(1, ManiaAction.Key2)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.5f,
+ Child = new ColumnHitObjectArea(1, new HitObjectContainer())
+ {
+ RelativeSizeAxes = Axes.Both
+ }
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs
similarity index 89%
rename from osu.Game.Rulesets.Mania.Tests/TestSceneDrawableJudgement.cs
rename to osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs
index 692d079c16..6ab8a68176 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableJudgement.cs
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs
@@ -6,15 +6,14 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
-using osu.Game.Tests.Visual;
-using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
-namespace osu.Game.Rulesets.Mania.Tests
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
{
- public class TestSceneDrawableJudgement : SkinnableTestScene
+ public class TestSceneDrawableJudgement : ManiaSkinnableTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs
new file mode 100644
index 0000000000..5f046574ba
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs
@@ -0,0 +1,66 @@
+// 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;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Skinning;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ [TestFixture]
+ public class TestSceneHitExplosion : ManiaSkinnableTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DrawableNote),
+ typeof(DrawableManiaHitObject),
+ };
+
+ public TestSceneHitExplosion()
+ {
+ int runcount = 0;
+
+ AddRepeatStep("explode", () =>
+ {
+ runcount++;
+
+ if (runcount % 15 > 12)
+ return;
+
+ CreatedDrawables.OfType().ForEach(c =>
+ {
+ c.Add(new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, 0),
+ _ => new DefaultHitExplosion((runcount / 15) % 2 == 0 ? new Color4(94, 0, 57, 255) : new Color4(6, 84, 0, 255), runcount % 6 != 0)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }));
+ });
+ }, 100);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ SetContents(() => new ColumnTestContainer(0, ManiaAction.Key1)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativePositionAxes = Axes.Y,
+ Y = -0.25f,
+ Size = new Vector2(Column.COLUMN_WIDTH, DefaultNotePiece.NOTE_HEIGHT),
+ });
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHoldNote.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHoldNote.cs
new file mode 100644
index 0000000000..95e86de884
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHoldNote.cs
@@ -0,0 +1,35 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using osu.Framework.Bindables;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ public class TestSceneHoldNote : ManiaHitObjectTestScene
+ {
+ public TestSceneHoldNote()
+ {
+ AddToggleStep("toggle hitting", v =>
+ {
+ foreach (var holdNote in CreatedDrawables.SelectMany(d => d.ChildrenOfType()))
+ {
+ ((Bindable)holdNote.IsHitting).Value = v;
+ }
+ });
+ }
+
+ protected override DrawableManiaHitObject CreateHitObject()
+ {
+ var note = new HoldNote { Duration = 1000 };
+ note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
+ return new DrawableHoldNote(note);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs
new file mode 100644
index 0000000000..c8f901285a
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs
@@ -0,0 +1,58 @@
+// 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 osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Mania.Skinning;
+using osu.Game.Rulesets.Mania.UI.Components;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ public class TestSceneKeyArea : ManiaSkinnableTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DefaultKeyArea),
+ typeof(LegacyKeyArea)
+ };
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ SetContents(() => new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(0.8f),
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ new ColumnTestContainer(0, ManiaAction.Key1)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.5f,
+ Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 0), _ => new DefaultKeyArea())
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ },
+ new ColumnTestContainer(1, ManiaAction.Key2)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.5f,
+ Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 1), _ => new DefaultKeyArea())
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ },
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneNote.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneNote.cs
new file mode 100644
index 0000000000..bc3bdf0bcb
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneNote.cs
@@ -0,0 +1,21 @@
+// 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.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ public class TestSceneNote : ManiaHitObjectTestScene
+ {
+ protected override DrawableManiaHitObject CreateHitObject()
+ {
+ var note = new Note();
+ note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
+ return new DrawableNote(note);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs
new file mode 100644
index 0000000000..161eda650e
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs
@@ -0,0 +1,52 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.UI;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ public class TestScenePlayfield : ManiaSkinnableTestScene
+ {
+ private List stageDefinitions = new List();
+
+ [Test]
+ public void TestSingleStage()
+ {
+ AddStep("create stage", () =>
+ {
+ stageDefinitions = new List
+ {
+ new StageDefinition { Columns = 2 }
+ };
+
+ SetContents(() => new ManiaPlayfield(stageDefinitions));
+ });
+ }
+
+ [Test]
+ public void TestDualStages()
+ {
+ AddStep("create stage", () =>
+ {
+ stageDefinitions = new List
+ {
+ new StageDefinition { Columns = 2 },
+ new StageDefinition { Columns = 2 }
+ };
+
+ SetContents(() => new ManiaPlayfield(stageDefinitions));
+ });
+ }
+
+ protected override IBeatmap CreateBeatmapForSkinProvider()
+ {
+ var maniaBeatmap = (ManiaBeatmap)base.CreateBeatmapForSkinProvider();
+ maniaBeatmap.Stages = stageDefinitions;
+ return maniaBeatmap;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs
new file mode 100644
index 0000000000..37b97a444a
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs
@@ -0,0 +1,27 @@
+// 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.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.UI;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ public class TestSceneStage : ManiaSkinnableTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ SetContents(() =>
+ {
+ ManiaAction normalAction = ManiaAction.Key1;
+ ManiaAction specialAction = ManiaAction.Special1;
+
+ return new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4)
+ {
+ Child = new Stage(0, new StageDefinition { Columns = 4 }, ref normalAction, ref specialAction)
+ };
+ });
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs
new file mode 100644
index 0000000000..a8fc68188a
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs
@@ -0,0 +1,35 @@
+// 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 osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Rulesets.Mania.Skinning;
+using osu.Game.Rulesets.Mania.UI.Components;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ public class TestSceneStageBackground : ManiaSkinnableTestScene
+ {
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[]
+ {
+ typeof(DefaultStageBackground),
+ typeof(LegacyStageBackground),
+ }).ToList();
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground), _ => new DefaultStageBackground())
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.5f,
+ });
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs
new file mode 100644
index 0000000000..d436445b59
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs
@@ -0,0 +1,33 @@
+// 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 osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Rulesets.Mania.Skinning;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
+{
+ public class TestSceneStageForeground : ManiaSkinnableTestScene
+ {
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[]
+ {
+ typeof(LegacyStageForeground),
+ }).ToList();
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.5f,
+ });
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs
index d94a986dae..5e06002f41 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs
@@ -28,8 +28,9 @@ namespace osu.Game.Rulesets.Mania.Tests
{
typeof(Column),
typeof(ColumnBackground),
- typeof(ColumnKeyArea),
- typeof(ColumnHitObjectArea)
+ typeof(ColumnHitObjectArea),
+ typeof(DefaultKeyArea),
+ typeof(DefaultHitTarget)
};
[Cached(typeof(IReadOnlyList))]
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs
deleted file mode 100644
index 26a1b1b1ec..0000000000
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs
+++ /dev/null
@@ -1,62 +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 System.Collections.Generic;
-using NUnit.Framework;
-using osu.Framework.Graphics;
-using osu.Game.Rulesets.Mania.Objects.Drawables;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
-using osu.Game.Rulesets.Mania.UI;
-using osu.Game.Rulesets.UI.Scrolling;
-using osu.Game.Tests.Visual;
-using osuTK;
-using osuTK.Graphics;
-
-namespace osu.Game.Rulesets.Mania.Tests
-{
- [TestFixture]
- public class TestSceneHitExplosion : OsuTestScene
- {
- private ScrollingTestContainer scrolling;
-
- public override IReadOnlyList RequiredTypes => new[]
- {
- typeof(DrawableNote),
- typeof(DrawableManiaHitObject),
- };
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- Child = scrolling = new ScrollingTestContainer(ScrollingDirection.Down)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativePositionAxes = Axes.Y,
- Y = -0.25f,
- Size = new Vector2(Column.COLUMN_WIDTH, NotePiece.NOTE_HEIGHT),
- };
-
- int runcount = 0;
-
- AddRepeatStep("explode", () =>
- {
- runcount++;
-
- if (runcount % 15 > 12)
- return;
-
- scrolling.AddRange(new Drawable[]
- {
- new HitExplosion((runcount / 15) % 2 == 0 ? new Color4(94, 0, 57, 255) : new Color4(6, 84, 0, 255), runcount % 6 != 0)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- }
- });
- }, 100);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs
index d5fd2808b8..7376a90f17 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Tests
[Cached(typeof(IReadOnlyList))]
private IReadOnlyList mods { get; set; } = Array.Empty();
- private readonly List stages = new List();
+ private readonly List stages = new List();
private FillFlowContainer fill;
@@ -81,9 +81,9 @@ namespace osu.Game.Rulesets.Mania.Tests
AddAssert("check bar anchors", () => barsInStageAreAnchored(stages[1], Anchor.TopCentre));
}
- private bool notesInStageAreAnchored(ManiaStage stage, Anchor anchor) => stage.Columns.SelectMany(c => c.AllHitObjects).All(o => o.Anchor == anchor);
+ private bool notesInStageAreAnchored(Stage stage, Anchor anchor) => stage.Columns.SelectMany(c => c.AllHitObjects).All(o => o.Anchor == anchor);
- private bool barsInStageAreAnchored(ManiaStage stage, Anchor anchor) => stage.AllHitObjects.Where(obj => obj is DrawableBarLine).All(o => o.Anchor == anchor);
+ private bool barsInStageAreAnchored(Stage stage, Anchor anchor) => stage.AllHitObjects.Where(obj => obj is DrawableBarLine).All(o => o.Anchor == anchor);
private void createNote()
{
@@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
var specialAction = ManiaAction.Special1;
- var stage = new ManiaStage(0, new StageDefinition { Columns = 2 }, ref action, ref specialAction);
+ var stage = new Stage(0, new StageDefinition { Columns = 2 }, ref action, ref specialAction);
stages.Add(stage);
return new ScrollingTestContainer(direction)
diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponents.cs b/osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs
similarity index 59%
rename from osu.Game.Rulesets.Mania/ManiaSkinComponents.cs
rename to osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs
index 6d85816e5a..8f904530bc 100644
--- a/osu.Game.Rulesets.Mania/ManiaSkinComponents.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs
@@ -1,9 +1,12 @@
// 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
+namespace osu.Game.Rulesets.Mania.Beatmaps
{
- public enum ManiaSkinComponents
+ public enum ColumnType
{
+ Even,
+ Odd,
+ Special
}
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs
index dff7cb72ce..2557f2acdf 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.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 osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Beatmaps
@@ -21,5 +22,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// The 0-based column index.
/// Whether the column is a special column.
public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
+
+ ///
+ /// Get the type of column given a column index.
+ ///
+ /// The 0-based column index.
+ /// The type of the column.
+ public ColumnType GetTypeOfColumn(int column)
+ {
+ if (IsSpecialColumn(column))
+ return ColumnType.Special;
+
+ int distanceToEdge = Math.Min(column, (Columns - 1) - column);
+ return distanceToEdge % 2 == 0 ? ColumnType.Odd : ColumnType.Even;
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
index f5412dcfc5..7e84f17809 100644
--- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
+++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.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 osu.Framework.Configuration.Tracking;
using osu.Game.Configuration;
using osu.Game.Rulesets.Configuration;
@@ -19,13 +20,14 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
base.InitialiseDefaults();
- Set(ManiaRulesetSetting.ScrollTime, 1500.0, 50.0, 5000.0, 50.0);
+ Set(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 1);
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{
- new TrackedSetting(ManiaRulesetSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
+ new TrackedSetting(ManiaRulesetSetting.ScrollTime,
+ v => new SettingDescription(v, "Scroll Speed", $"{(int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / v)} ({v}ms)"))
};
}
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditBodyPiece.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditBodyPiece.cs
index b99a1157f3..efcfe11dad 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditBodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditBodyPiece.cs
@@ -7,12 +7,12 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints.Components
{
- public class EditBodyPiece : BodyPiece
+ public class EditBodyPiece : DefaultBodyPiece
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- AccentColour = colours.Yellow;
+ AccentColour.Value = colours.Yellow;
Background.Alpha = 0.5f;
Foreground.Alpha = 0;
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditNotePiece.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditNotePiece.cs
index 6f85fd9167..8773a39939 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditNotePiece.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/Components/EditNotePiece.cs
@@ -12,12 +12,12 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints.Components
{
public EditNotePiece()
{
- Height = NotePiece.NOTE_HEIGHT;
+ Height = DefaultNotePiece.NOTE_HEIGHT;
CornerRadius = 5;
Masking = true;
- InternalChild = new NotePiece();
+ InternalChild = new DefaultNotePiece();
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs
index 56c0b671a0..f1750f4a01 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs
@@ -4,13 +4,13 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK;
-using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
{
@@ -42,11 +42,18 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
{
new HoldNoteNoteSelectionBlueprint(DrawableObject, HoldNotePosition.Start),
new HoldNoteNoteSelectionBlueprint(DrawableObject, HoldNotePosition.End),
- new BodyPiece
+ new Container
{
- AccentColour = Color4.Transparent,
- BorderColour = colours.Yellow
- },
+ RelativeSizeAxes = Axes.Both,
+ BorderThickness = 1,
+ BorderColour = colours.Yellow,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true,
+ }
+ }
};
}
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs
index a3657d3bb9..6ddf212266 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs
@@ -122,11 +122,11 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
switch (scrollingInfo.Direction.Value)
{
case ScrollingDirection.Up:
- mousePosition.Y -= NotePiece.NOTE_HEIGHT / 2;
+ mousePosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2;
break;
case ScrollingDirection.Down:
- mousePosition.Y += NotePiece.NOTE_HEIGHT / 2;
+ mousePosition.Y += DefaultNotePiece.NOTE_HEIGHT / 2;
break;
}
@@ -143,11 +143,11 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
switch (scrollingInfo.Direction.Value)
{
case ScrollingDirection.Up:
- hitObjectPosition.Y += NotePiece.NOTE_HEIGHT / 2;
+ hitObjectPosition.Y += DefaultNotePiece.NOTE_HEIGHT / 2;
break;
case ScrollingDirection.Down:
- hitObjectPosition.Y -= NotePiece.NOTE_HEIGHT / 2;
+ hitObjectPosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2;
break;
}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 9d06bd7c25..2bd88fee90 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania
public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
- public override ISkin CreateLegacySkinProvider(ISkinSource source) => new ManiaLegacySkinTransformer(source);
+ public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new ManiaLegacySkinTransformer(source, beatmap);
public override IEnumerable ConvertFromLegacyMods(LegacyMods mods)
{
diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs
index 69bd4b0ecf..c0c8505f44 100644
--- a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs
+++ b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs
@@ -1,19 +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.Game.Rulesets.Mania.UI;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania
{
public class ManiaSkinComponent : GameplaySkinComponent
{
- public ManiaSkinComponent(ManiaSkinComponents component)
+ ///
+ /// The intended index for this component.
+ /// May be null if the component does not exist in a .
+ ///
+ public readonly int? TargetColumn;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The component.
+ /// The intended index for this component. May be null if the component does not exist in a .
+ public ManiaSkinComponent(ManiaSkinComponents component, int? targetColumn = null)
: base(component)
{
+ TargetColumn = targetColumn;
}
protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower();
}
+
+ public enum ManiaSkinComponents
+ {
+ ColumnBackground,
+ HitTarget,
+ KeyArea,
+ Note,
+ HoldNoteHead,
+ HoldNoteTail,
+ HoldNoteBody,
+ HitExplosion,
+ StageBackground,
+ StageForeground,
+ }
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 14a7c5fda3..a9ef661aaa 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -10,6 +10,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
@@ -20,6 +21,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public override bool DisplayResult => false;
+ public IBindable IsHitting => isHitting;
+
+ private readonly Bindable isHitting = new Bindable();
+
public DrawableHoldNoteHead Head => headContainer.Child;
public DrawableHoldNoteTail Tail => tailContainer.Child;
@@ -27,7 +32,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
private readonly Container tailContainer;
private readonly Container tickContainer;
- private readonly BodyPiece bodyPiece;
+ private readonly Drawable bodyPiece;
///
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
@@ -44,18 +49,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
RelativeSizeAxes = Axes.X;
- AddRangeInternal(new Drawable[]
+ AddRangeInternal(new[]
{
- bodyPiece = new BodyPiece { RelativeSizeAxes = Axes.X },
+ bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece())
+ {
+ RelativeSizeAxes = Axes.X
+ },
tickContainer = new Container { RelativeSizeAxes = Axes.Both },
headContainer = new Container { RelativeSizeAxes = Axes.Both },
tailContainer = new Container { RelativeSizeAxes = Axes.Both },
});
-
- AccentColour.BindValueChanged(colour =>
- {
- bodyPiece.AccentColour = colour.NewValue;
- }, true);
}
protected override void AddNestedHitObject(DrawableHitObject hitObject)
@@ -168,7 +171,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return;
HoldStartTime = Time.Current;
- bodyPiece.Hitting = true;
+ isHitting.Value = true;
}
public void OnReleased(ManiaAction action)
@@ -194,7 +197,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
private void endHold()
{
HoldStartTime = null;
- bodyPiece.Hitting = false;
+ isHitting.Value = false;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
index 390c64c5e2..a73fe259e4 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
@@ -8,6 +8,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
public class DrawableHoldNoteHead : DrawableNote
{
+ protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteHead;
+
public DrawableHoldNoteHead(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Head)
{
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
index 568b07c958..31e43d3ee2 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
@@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
private const double release_window_lenience = 1.5;
+ protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteTail;
+
private readonly DrawableHoldNote holdNote;
public DrawableHoldNoteTail(DrawableHoldNote holdNote)
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 85613d3afb..9451bc4430 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -3,13 +3,12 @@
using System.Diagnostics;
using osu.Framework.Bindables;
-using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Effects;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
@@ -18,7 +17,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
public class DrawableNote : DrawableManiaHitObject, IKeyBindingHandler
{
- private readonly NotePiece headPiece;
+ protected virtual ManiaSkinComponents Component => ManiaSkinComponents.Note;
+
+ private readonly Drawable headPiece;
public DrawableNote(Note hitObject)
: base(hitObject)
@@ -26,22 +27,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
- CornerRadius = 5;
- Masking = true;
-
- AddInternal(headPiece = new NotePiece());
-
- AccentColour.BindValueChanged(colour =>
+ AddInternal(headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component, hitObject.Column), _ => new DefaultNotePiece())
{
- headPiece.AccentColour = colour.NewValue;
-
- EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Glow,
- Colour = colour.NewValue.Lighten(1f).Opacity(0.2f),
- Radius = 10,
- };
- }, true);
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y
+ });
}
protected override void OnDirectionChanged(ValueChangedEvent e)
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs
similarity index 70%
rename from osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
rename to osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs
index 43f9ae2783..0ee0a14df3 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultBodyPiece.cs
@@ -2,6 +2,9 @@
// 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 osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -9,26 +12,38 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Layout;
-using osu.Game.Graphics;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
///
/// Represents length-wise portion of a hold note.
///
- public class BodyPiece : Container, IHasAccentColour
+ public class DefaultBodyPiece : CompositeDrawable
{
- private readonly Container subtractionLayer;
+ protected readonly Bindable AccentColour = new Bindable();
- protected readonly Drawable Background;
- protected readonly BufferedContainer Foreground;
- private readonly BufferedContainer subtractionContainer;
+ private readonly LayoutValue subtractionCache = new LayoutValue(Invalidation.DrawSize);
+ private readonly IBindable isHitting = new Bindable();
- public BodyPiece()
+ protected Drawable Background { get; private set; }
+ protected BufferedContainer Foreground { get; private set; }
+
+ private BufferedContainer subtractionContainer;
+ private Container subtractionLayer;
+
+ public DefaultBodyPiece()
{
+ RelativeSizeAxes = Axes.Both;
Blending = BlendingParameters.Additive;
- Children = new[]
+ AddLayout(subtractionCache);
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load([CanBeNull] DrawableHitObject drawableObject)
+ {
+ InternalChildren = new[]
{
Background = new Box { RelativeSizeAxes = Axes.Both },
Foreground = new BufferedContainer
@@ -66,43 +81,37 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
};
- AddLayout(subtractionCache);
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- updateAccentColour();
- }
-
- private Color4 accentColour;
-
- public Color4 AccentColour
- {
- get => accentColour;
- set
+ if (drawableObject != null)
{
- if (accentColour == value)
- return;
+ var holdNote = (DrawableHoldNote)drawableObject;
- accentColour = value;
-
- updateAccentColour();
+ AccentColour.BindTo(drawableObject.AccentColour);
+ isHitting.BindTo(holdNote.IsHitting);
}
+
+ AccentColour.BindValueChanged(onAccentChanged, true);
+ isHitting.BindValueChanged(_ => onAccentChanged(new ValueChangedEvent(AccentColour.Value, AccentColour.Value)), true);
}
- public bool Hitting
+ private void onAccentChanged(ValueChangedEvent accent)
{
- get => hitting;
- set
- {
- hitting = value;
- updateAccentColour();
- }
- }
+ Foreground.Colour = accent.NewValue.Opacity(0.5f);
+ Background.Colour = accent.NewValue.Opacity(0.7f);
- private readonly LayoutValue subtractionCache = new LayoutValue(Invalidation.DrawSize);
+ const float animation_length = 50;
+
+ Foreground.ClearTransforms(false, nameof(Foreground.Colour));
+
+ if (isHitting.Value)
+ {
+ // wait for the next sync point
+ double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2);
+ using (Foreground.BeginDelayedSequence(synchronisedOffset))
+ Foreground.FadeColour(accent.NewValue.Lighten(0.2f), animation_length).Then().FadeColour(Foreground.Colour, animation_length).Loop();
+ }
+
+ subtractionCache.Invalidate();
+ }
protected override void Update()
{
@@ -125,30 +134,5 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
subtractionCache.Validate();
}
}
-
- private bool hitting;
-
- private void updateAccentColour()
- {
- if (!IsLoaded)
- return;
-
- Foreground.Colour = AccentColour.Opacity(0.5f);
- Background.Colour = AccentColour.Opacity(0.7f);
-
- const float animation_length = 50;
-
- Foreground.ClearTransforms(false, nameof(Foreground.Colour));
-
- if (hitting)
- {
- // wait for the next sync point
- double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2);
- using (Foreground.BeginDelayedSequence(synchronisedOffset))
- Foreground.FadeColour(AccentColour.Lighten(0.2f), animation_length).Then().FadeColour(Foreground.Colour, animation_length).Loop();
- }
-
- subtractionCache.Invalidate();
- }
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultNotePiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultNotePiece.cs
new file mode 100644
index 0000000000..29f5217fd8
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/DefaultNotePiece.cs
@@ -0,0 +1,85 @@
+// 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.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.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.UI.Scrolling;
+
+namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
+{
+ ///
+ /// Represents the static hit markers of notes.
+ ///
+ internal class DefaultNotePiece : CompositeDrawable
+ {
+ public const float NOTE_HEIGHT = 12;
+
+ private readonly IBindable direction = new Bindable();
+ private readonly IBindable accentColour = new Bindable();
+
+ private readonly Box colouredBox;
+
+ public DefaultNotePiece()
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = NOTE_HEIGHT;
+
+ CornerRadius = 5;
+ Masking = true;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ colouredBox = new Box
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = NOTE_HEIGHT / 2,
+ Alpha = 0.1f
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load([NotNull] IScrollingInfo scrollingInfo, [CanBeNull] DrawableHitObject drawableObject)
+ {
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+
+ if (drawableObject != null)
+ {
+ accentColour.BindTo(drawableObject.AccentColour);
+ accentColour.BindValueChanged(onAccentChanged, true);
+ }
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up
+ ? Anchor.TopCentre
+ : Anchor.BottomCentre;
+ }
+
+ private void onAccentChanged(ValueChangedEvent accent)
+ {
+ colouredBox.Colour = accent.NewValue.Lighten(0.9f);
+
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = accent.NewValue.Lighten(1f).Opacity(0.2f),
+ Radius = 10,
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
deleted file mode 100644
index 4521af7dfb..0000000000
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/NotePiece.cs
+++ /dev/null
@@ -1,73 +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 osuTK.Graphics;
-using osu.Framework.Extensions.Color4Extensions;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.UI.Scrolling;
-
-namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
-{
- ///
- /// Represents the static hit markers of notes.
- ///
- internal class NotePiece : Container, IHasAccentColour
- {
- public const float NOTE_HEIGHT = 12;
-
- private readonly IBindable direction = new Bindable();
-
- private readonly Box colouredBox;
-
- public NotePiece()
- {
- RelativeSizeAxes = Axes.X;
- Height = NOTE_HEIGHT;
-
- Children = new[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both
- },
- colouredBox = new Box
- {
- RelativeSizeAxes = Axes.X,
- Height = NOTE_HEIGHT / 2,
- Alpha = 0.1f
- }
- };
- }
-
- [BackgroundDependencyLoader]
- private void load(IScrollingInfo scrollingInfo)
- {
- direction.BindTo(scrollingInfo.Direction);
- direction.BindValueChanged(dir =>
- {
- colouredBox.Anchor = colouredBox.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
- }, true);
- }
-
- private Color4 accentColour;
-
- public Color4 AccentColour
- {
- get => accentColour;
- set
- {
- if (accentColour == value)
- return;
-
- accentColour = value;
-
- colouredBox.Colour = AccentColour.Lighten(0.9f);
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyBodyPiece.cs
new file mode 100644
index 0000000000..0c9bc97ba9
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyBodyPiece.cs
@@ -0,0 +1,86 @@
+// 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.Animations;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyBodyPiece : LegacyManiaColumnElement
+ {
+ private readonly IBindable direction = new Bindable();
+ private readonly IBindable isHitting = new Bindable();
+
+ private Drawable sprite;
+
+ public LegacyBodyPiece()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin, IScrollingInfo scrollingInfo, DrawableHitObject drawableObject)
+ {
+ string imageName = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.HoldNoteBodyImage)?.Value
+ ?? $"mania-note{FallbackColumnIndex}L";
+
+ sprite = skin.GetAnimation(imageName, true, true).With(d =>
+ {
+ if (d == null)
+ return;
+
+ if (d is TextureAnimation animation)
+ animation.IsPlaying = false;
+
+ d.Anchor = Anchor.TopCentre;
+ d.RelativeSizeAxes = Axes.Both;
+ d.Size = Vector2.One;
+ d.FillMode = FillMode.Stretch;
+ // Todo: Wrap
+ });
+
+ if (sprite != null)
+ InternalChild = sprite;
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+
+ var holdNote = (DrawableHoldNote)drawableObject;
+ isHitting.BindTo(holdNote.IsHitting);
+ isHitting.BindValueChanged(onIsHittingChanged, true);
+ }
+
+ private void onIsHittingChanged(ValueChangedEvent isHitting)
+ {
+ if (!(sprite is TextureAnimation animation))
+ return;
+
+ animation.GotoFrame(0);
+ animation.IsPlaying = isHitting.NewValue;
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (sprite == null)
+ return;
+
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ sprite.Origin = Anchor.BottomCentre;
+ sprite.Scale = new Vector2(1, -1);
+ }
+ else
+ {
+ sprite.Origin = Anchor.TopCentre;
+ sprite.Scale = Vector2.One;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs
new file mode 100644
index 0000000000..6504321bb2
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyColumnBackground.cs
@@ -0,0 +1,141 @@
+// 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.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyColumnBackground : LegacyManiaColumnElement, IKeyBindingHandler
+ {
+ private readonly IBindable direction = new Bindable();
+ private readonly bool isLastColumn;
+
+ private Container lightContainer;
+ private Sprite light;
+
+ public LegacyColumnBackground(bool isLastColumn)
+ {
+ this.isLastColumn = isLastColumn;
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
+ {
+ string lightImage = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.LightImage, 0)?.Value
+ ?? "mania-stage-light";
+
+ float leftLineWidth = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.LeftLineWidth)
+ ?.Value ?? 1;
+ float rightLineWidth = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.RightLineWidth)
+ ?.Value ?? 1;
+
+ bool hasLeftLine = leftLineWidth > 0;
+ bool hasRightLine = rightLineWidth > 0 && skin.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
+ || isLastColumn;
+
+ float lightPosition = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.LightPosition)?.Value
+ ?? 0;
+
+ Color4 lineColour = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.ColumnLineColour)?.Value
+ ?? Color4.White;
+
+ Color4 backgroundColour = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour)?.Value
+ ?? Color4.Black;
+
+ Color4 lightColour = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.ColumnLightColour)?.Value
+ ?? Color4.White;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = backgroundColour
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.Y,
+ Width = leftLineWidth,
+ Colour = lineColour,
+ Alpha = hasLeftLine ? 1 : 0
+ },
+ new Box
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ RelativeSizeAxes = Axes.Y,
+ Width = rightLineWidth,
+ Colour = lineColour,
+ Alpha = hasRightLine ? 1 : 0
+ },
+ lightContainer = new Container
+ {
+ Origin = Anchor.BottomCentre,
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Bottom = lightPosition },
+ Child = light = new Sprite
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ Colour = lightColour,
+ Texture = skin.GetTexture(lightImage),
+ RelativeSizeAxes = Axes.X,
+ Width = 1,
+ Alpha = 0
+ }
+ }
+ };
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ lightContainer.Anchor = Anchor.TopCentre;
+ lightContainer.Scale = new Vector2(1, -1);
+ }
+ else
+ {
+ lightContainer.Anchor = Anchor.BottomCentre;
+ lightContainer.Scale = Vector2.One;
+ }
+ }
+
+ public bool OnPressed(ManiaAction action)
+ {
+ if (action == Column.Action.Value)
+ {
+ light.FadeIn();
+ light.ScaleTo(Vector2.One);
+ }
+
+ return false;
+ }
+
+ public void OnReleased(ManiaAction action)
+ {
+ // Todo: Should be 400 * 100 / CurrentBPM
+ const double animation_length = 250;
+
+ if (action == Column.Action.Value)
+ {
+ light.FadeTo(0, animation_length);
+ light.ScaleTo(new Vector2(1, 0), animation_length);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs
new file mode 100644
index 0000000000..ce0b9fe4b6
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs
@@ -0,0 +1,73 @@
+// 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.Framework.Graphics.Animations;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyHitExplosion : LegacyManiaColumnElement
+ {
+ private readonly IBindable direction = new Bindable();
+
+ private Drawable explosion;
+
+ public LegacyHitExplosion()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
+ {
+ string imageName = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.ExplosionImage)?.Value
+ ?? "lightingN";
+
+ float explosionScale = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.ExplosionScale)?.Value
+ ?? 1;
+
+ // Create a temporary animation to retrieve the number of frames, in an effort to calculate the intended frame length.
+ // This animation is discarded and re-queried with the appropriate frame length afterwards.
+ var tmp = skin.GetAnimation(imageName, true, false);
+ double frameLength = 0;
+ if (tmp is IFramedAnimation tmpAnimation && tmpAnimation.FrameCount > 0)
+ frameLength = Math.Max(1000 / 60.0, 170.0 / tmpAnimation.FrameCount);
+
+ explosion = skin.GetAnimation(imageName, true, false, frameLength: frameLength).With(d =>
+ {
+ if (d == null)
+ return;
+
+ d.Origin = Anchor.Centre;
+ d.Blending = BlendingParameters.Additive;
+ d.Scale = new Vector2(explosionScale);
+ });
+
+ if (explosion != null)
+ InternalChild = explosion;
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (explosion != null)
+ explosion.Anchor = direction.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ explosion?.FadeInFromZero(80)
+ .Then().FadeOut(120);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHitTarget.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyHitTarget.cs
new file mode 100644
index 0000000000..40752d3f4b
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyHitTarget.cs
@@ -0,0 +1,83 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyHitTarget : LegacyManiaElement
+ {
+ private readonly IBindable direction = new Bindable();
+
+ private Container directionContainer;
+
+ public LegacyHitTarget()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
+ {
+ string targetImage = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.HitTargetImage)?.Value
+ ?? "mania-stage-hint";
+
+ bool showJudgementLine = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.ShowJudgementLine)?.Value
+ ?? true;
+
+ Color4 lineColour = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.JudgementLineColour)?.Value
+ ?? Color4.White;
+
+ InternalChild = directionContainer = new Container
+ {
+ Origin = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ new Sprite
+ {
+ Texture = skin.GetTexture(targetImage),
+ Scale = new Vector2(1, 0.9f * 1.6025f),
+ RelativeSizeAxes = Axes.X,
+ Width = 1
+ },
+ new Box
+ {
+ Anchor = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = 1,
+ Colour = lineColour,
+ Alpha = showJudgementLine ? 0.9f : 0
+ }
+ }
+ };
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ directionContainer.Anchor = Anchor.TopLeft;
+ directionContainer.Scale = new Vector2(1, -1);
+ }
+ else
+ {
+ directionContainer.Anchor = Anchor.BottomLeft;
+ directionContainer.Scale = Vector2.One;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteHeadPiece.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteHeadPiece.cs
new file mode 100644
index 0000000000..c5aa062d0f
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteHeadPiece.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.Framework.Graphics.Textures;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyHoldNoteHeadPiece : LegacyNotePiece
+ {
+ protected override Texture GetTexture(ISkinSource skin)
+ {
+ // TODO: Should fallback to the head from default legacy skin instead of note.
+ return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
+ ?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteTailPiece.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteTailPiece.cs
new file mode 100644
index 0000000000..2e8259f10a
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyHoldNoteTailPiece.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.Framework.Graphics.Textures;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyHoldNoteTailPiece : LegacyNotePiece
+ {
+ protected override void OnDirectionChanged(ValueChangedEvent direction)
+ {
+ // Invert the direction
+ base.OnDirectionChanged(direction.NewValue == ScrollingDirection.Up
+ ? new ValueChangedEvent(ScrollingDirection.Down, ScrollingDirection.Down)
+ : new ValueChangedEvent(ScrollingDirection.Up, ScrollingDirection.Up));
+ }
+
+ protected override Texture GetTexture(ISkinSource skin)
+ {
+ // TODO: Should fallback to the head from default legacy skin instead of note.
+ return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteTailImage)
+ ?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
+ ?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyKeyArea.cs
new file mode 100644
index 0000000000..7c8d1cd303
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyKeyArea.cs
@@ -0,0 +1,106 @@
+// 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.Framework.Input.Bindings;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyKeyArea : LegacyManiaColumnElement, IKeyBindingHandler
+ {
+ private readonly IBindable direction = new Bindable();
+
+ private Container directionContainer;
+ private Sprite upSprite;
+ private Sprite downSprite;
+
+ [Resolved]
+ private Column column { get; set; }
+
+ public LegacyKeyArea()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
+ {
+ string upImage = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.KeyImage)?.Value
+ ?? $"mania-key{FallbackColumnIndex}";
+
+ string downImage = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.KeyImageDown)?.Value
+ ?? $"mania-key{FallbackColumnIndex}D";
+
+ InternalChild = directionContainer = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ upSprite = new Sprite
+ {
+ Origin = Anchor.BottomCentre,
+ Texture = skin.GetTexture(upImage),
+ RelativeSizeAxes = Axes.X,
+ Width = 1
+ },
+ downSprite = new Sprite
+ {
+ Origin = Anchor.BottomCentre,
+ Texture = skin.GetTexture(downImage),
+ RelativeSizeAxes = Axes.X,
+ Width = 1,
+ Alpha = 0
+ }
+ }
+ };
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ directionContainer.Anchor = directionContainer.Origin = Anchor.TopCentre;
+ upSprite.Anchor = downSprite.Anchor = Anchor.TopCentre;
+ upSprite.Scale = downSprite.Scale = new Vector2(1, -1);
+ }
+ else
+ {
+ directionContainer.Anchor = directionContainer.Origin = Anchor.BottomCentre;
+ upSprite.Anchor = downSprite.Anchor = Anchor.BottomCentre;
+ upSprite.Scale = downSprite.Scale = Vector2.One;
+ }
+ }
+
+ public bool OnPressed(ManiaAction action)
+ {
+ if (action == column.Action.Value)
+ {
+ upSprite.FadeTo(0);
+ downSprite.FadeTo(1);
+ }
+
+ return false;
+ }
+
+ public void OnReleased(ManiaAction action)
+ {
+ if (action == column.Action.Value)
+ {
+ upSprite.FadeTo(1);
+ downSprite.FadeTo(0);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyManiaColumnElement.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyManiaColumnElement.cs
new file mode 100644
index 0000000000..05b731ec5d
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyManiaColumnElement.cs
@@ -0,0 +1,48 @@
+// 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.Containers;
+using osu.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ ///
+ /// A which is placed somewhere within a .
+ ///
+ public class LegacyManiaColumnElement : LegacyManiaElement
+ {
+ [Resolved]
+ protected Column Column { get; private set; }
+
+ ///
+ /// The column type identifier to use for texture lookups, in the case of no user-provided configuration.
+ ///
+ protected string FallbackColumnIndex { get; private set; }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ switch (Column.ColumnType)
+ {
+ case ColumnType.Special:
+ FallbackColumnIndex = "S";
+ break;
+
+ case ColumnType.Odd:
+ FallbackColumnIndex = "1";
+ break;
+
+ case ColumnType.Even:
+ FallbackColumnIndex = "2";
+ break;
+ }
+ }
+
+ protected override IBindable GetManiaSkinConfig(ISkin skin, LegacyManiaSkinConfigurationLookups lookup, int? index = null)
+ => base.GetManiaSkinConfig(skin, lookup, index ?? Column.Index);
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyManiaElement.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyManiaElement.cs
new file mode 100644
index 0000000000..11fdd663a1
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyManiaElement.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 osu.Framework.Graphics.Containers;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ ///
+ /// A mania legacy skin element.
+ ///
+ public class LegacyManiaElement : CompositeDrawable
+ {
+ ///
+ /// Retrieve a per-column-count skin configuration.
+ ///
+ /// The skin from which configuration is retrieved.
+ /// The value to retrieve.
+ /// If not null, denotes the index of the column to which the entry applies.
+ protected virtual IBindable GetManiaSkinConfig(ISkin skin, LegacyManiaSkinConfigurationLookups lookup, int? index = null)
+ => skin.GetConfig(
+ new ManiaSkinConfigurationLookup(lookup, index));
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyNotePiece.cs
new file mode 100644
index 0000000000..85523ae3c0
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyNotePiece.cs
@@ -0,0 +1,98 @@
+// 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.Framework.Graphics.Textures;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyNotePiece : LegacyManiaColumnElement
+ {
+ private readonly IBindable direction = new Bindable();
+
+ private Container directionContainer;
+ private Sprite noteSprite;
+
+ private float? minimumColumnWidth;
+
+ public LegacyNotePiece()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
+ {
+ minimumColumnWidth = skin.GetConfig(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.MinimumColumnWidth))?.Value;
+
+ InternalChild = directionContainer = new Container
+ {
+ Origin = Anchor.BottomCentre,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Child = noteSprite = new Sprite { Texture = GetTexture(skin) }
+ };
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(OnDirectionChanged, true);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (noteSprite.Texture != null)
+ {
+ // The height is scaled to the minimum column width, if provided.
+ float minimumWidth = minimumColumnWidth ?? DrawWidth;
+
+ noteSprite.Scale = Vector2.Divide(new Vector2(DrawWidth, minimumWidth), noteSprite.Texture.DisplayWidth);
+ }
+ }
+
+ protected virtual void OnDirectionChanged(ValueChangedEvent direction)
+ {
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ directionContainer.Anchor = Anchor.TopCentre;
+ directionContainer.Scale = new Vector2(1, -1);
+ }
+ else
+ {
+ directionContainer.Anchor = Anchor.BottomCentre;
+ directionContainer.Scale = Vector2.One;
+ }
+ }
+
+ protected virtual Texture GetTexture(ISkinSource skin) => GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
+
+ protected Texture GetTextureFromLookup(ISkin skin, LegacyManiaSkinConfigurationLookups lookup)
+ {
+ string suffix = string.Empty;
+
+ switch (lookup)
+ {
+ case LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage:
+ suffix = "H";
+ break;
+
+ case LegacyManiaSkinConfigurationLookups.HoldNoteTailImage:
+ suffix = "T";
+ break;
+ }
+
+ string noteImage = GetManiaSkinConfig(skin, lookup)?.Value
+ ?? $"mania-note{FallbackColumnIndex}{suffix}";
+
+ return skin.GetTexture(noteImage);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs
new file mode 100644
index 0000000000..7680526ac4
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyStageBackground.cs
@@ -0,0 +1,61 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyStageBackground : LegacyManiaElement
+ {
+ private Drawable leftSprite;
+ private Drawable rightSprite;
+
+ public LegacyStageBackground()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin)
+ {
+ string leftImage = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.LeftStageImage)?.Value
+ ?? "mania-stage-left";
+
+ string rightImage = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.RightStageImage)?.Value
+ ?? "mania-stage-right";
+
+ InternalChildren = new[]
+ {
+ leftSprite = new Sprite
+ {
+ Anchor = Anchor.TopLeft,
+ Origin = Anchor.TopRight,
+ X = 0.05f,
+ Texture = skin.GetTexture(leftImage),
+ },
+ rightSprite = new Sprite
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopLeft,
+ X = -0.05f,
+ Texture = skin.GetTexture(rightImage)
+ }
+ };
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (leftSprite?.Height > 0)
+ leftSprite.Scale = new Vector2(DrawHeight / leftSprite.Height);
+
+ if (rightSprite?.Height > 0)
+ rightSprite.Scale = new Vector2(DrawHeight / rightSprite.Height);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyStageForeground.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyStageForeground.cs
new file mode 100644
index 0000000000..9719005d54
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyStageForeground.cs
@@ -0,0 +1,56 @@
+// 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.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyStageForeground : LegacyManiaElement
+ {
+ private readonly IBindable direction = new Bindable();
+
+ private Drawable sprite;
+
+ public LegacyStageForeground()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
+ {
+ string bottomImage = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.BottomStageImage)?.Value
+ ?? "mania-stage-bottom";
+
+ sprite = skin.GetAnimation(bottomImage, true, true)?.With(d =>
+ {
+ if (d == null)
+ return;
+
+ d.Scale = new Vector2(1.6f);
+ });
+
+ if (sprite != null)
+ InternalChild = sprite;
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (sprite == null)
+ return;
+
+ if (direction.NewValue == ScrollingDirection.Up)
+ sprite.Anchor = sprite.Origin = Anchor.TopCentre;
+ else
+ sprite.Anchor = sprite.Origin = Anchor.BottomCentre;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs
index f3739ce7c2..e64178083a 100644
--- a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs
@@ -1,12 +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 System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Audio;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning
@@ -14,10 +17,32 @@ namespace osu.Game.Rulesets.Mania.Skinning
public class ManiaLegacySkinTransformer : ISkin
{
private readonly ISkin source;
+ private readonly ManiaBeatmap beatmap;
- public ManiaLegacySkinTransformer(ISkin source)
+ private Lazy isLegacySkin;
+
+ ///
+ /// Whether texture for the keys exists.
+ /// Used to determine if the mania ruleset is skinned.
+ ///
+ private Lazy hasKeyTexture;
+
+ public ManiaLegacySkinTransformer(ISkinSource source, IBeatmap beatmap)
{
this.source = source;
+ this.beatmap = (ManiaBeatmap)beatmap;
+
+ source.SourceChanged += sourceChanged;
+ sourceChanged();
+ }
+
+ private void sourceChanged()
+ {
+ isLegacySkin = new Lazy(() => source.GetConfig(LegacySkinConfiguration.LegacySetting.Version) != null);
+ hasKeyTexture = new Lazy(() => source.GetAnimation(
+ GetConfig(
+ new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.KeyImage, 0))?.Value
+ ?? "mania-key1", true, true) != null);
}
public Drawable GetDrawableComponent(ISkinComponent component)
@@ -26,6 +51,45 @@ namespace osu.Game.Rulesets.Mania.Skinning
{
case GameplaySkinComponent resultComponent:
return getResult(resultComponent);
+
+ case ManiaSkinComponent maniaComponent:
+ if (!isLegacySkin.Value || !hasKeyTexture.Value)
+ return null;
+
+ switch (maniaComponent.Component)
+ {
+ case ManiaSkinComponents.ColumnBackground:
+ return new LegacyColumnBackground(maniaComponent.TargetColumn == beatmap.TotalColumns - 1);
+
+ case ManiaSkinComponents.HitTarget:
+ return new LegacyHitTarget();
+
+ case ManiaSkinComponents.KeyArea:
+ return new LegacyKeyArea();
+
+ case ManiaSkinComponents.Note:
+ return new LegacyNotePiece();
+
+ case ManiaSkinComponents.HoldNoteHead:
+ return new LegacyHoldNoteHeadPiece();
+
+ case ManiaSkinComponents.HoldNoteTail:
+ return new LegacyHoldNoteTailPiece();
+
+ case ManiaSkinComponents.HoldNoteBody:
+ return new LegacyBodyPiece();
+
+ case ManiaSkinComponents.HitExplosion:
+ return new LegacyHitExplosion();
+
+ case ManiaSkinComponents.StageBackground:
+ return new LegacyStageBackground();
+
+ case ManiaSkinComponents.StageForeground:
+ return new LegacyStageForeground();
+ }
+
+ break;
}
return null;
@@ -61,7 +125,12 @@ namespace osu.Game.Rulesets.Mania.Skinning
public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample);
- public IBindable GetConfig(TLookup lookup) =>
- source.GetConfig(lookup);
+ public IBindable GetConfig(TLookup lookup)
+ {
+ if (lookup is ManiaSkinConfigurationLookup maniaLookup)
+ return source.GetConfig(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.TargetColumn));
+
+ return source.GetConfig(lookup);
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs
new file mode 100644
index 0000000000..f07a5518b7
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs
@@ -0,0 +1,33 @@
+// 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.Mania.UI;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class ManiaSkinConfigurationLookup
+ {
+ ///
+ /// The configuration lookup value.
+ ///
+ public readonly LegacyManiaSkinConfigurationLookups Lookup;
+
+ ///
+ /// The intended index for the configuration.
+ /// May be null if the configuration does not apply to a .
+ ///
+ public readonly int? TargetColumn;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The lookup value.
+ /// The intended index for the configuration. May be null if the configuration does not apply to a .
+ public ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups lookup, int? targetColumn = null)
+ {
+ Lookup = lookup;
+ TargetColumn = targetColumn;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 63c573d344..506a07f26b 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -12,17 +12,19 @@ using osu.Framework.Bindables;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Objects.Drawables;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
using osuTK;
+using osu.Game.Rulesets.Mania.Beatmaps;
namespace osu.Game.Rulesets.Mania.UI
{
+ [Cached]
public class Column : ScrollingPlayfield, IKeyBindingHandler, IHasAccentColour
{
public const float COLUMN_WIDTH = 80;
- private const float special_column_width = 70;
+ public const float SPECIAL_COLUMN_WIDTH = 70;
///
/// The index of this column as part of the whole playfield.
@@ -31,12 +33,9 @@ namespace osu.Game.Rulesets.Mania.UI
public readonly Bindable Action = new Bindable();
- private readonly ColumnBackground background;
- private readonly ColumnKeyArea keyArea;
private readonly ColumnHitObjectArea hitObjectArea;
internal readonly Container TopLevelContainer;
- private readonly Container explosionContainer;
public Column(int index)
{
@@ -45,95 +44,34 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Y;
Width = COLUMN_WIDTH;
- background = new ColumnBackground { RelativeSizeAxes = Axes.Both };
-
- Container hitTargetContainer;
+ Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, Index), _ => new DefaultColumnBackground())
+ {
+ RelativeSizeAxes = Axes.Both
+ };
InternalChildren = new[]
{
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
background.CreateProxy(),
- hitTargetContainer = new Container
+ hitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both },
+ new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, Index), _ => new DefaultKeyArea())
{
- Name = "Hit target + hit objects",
- RelativeSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- hitObjectArea = new ColumnHitObjectArea(HitObjectContainer)
- {
- RelativeSizeAxes = Axes.Both,
- },
- explosionContainer = new Container
- {
- Name = "Hit explosions",
- RelativeSizeAxes = Axes.Both,
- }
- }
- },
- keyArea = new ColumnKeyArea
- {
- RelativeSizeAxes = Axes.X,
- Height = ManiaStage.HIT_TARGET_POSITION,
+ RelativeSizeAxes = Axes.Both
},
background,
TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
};
- TopLevelContainer.Add(explosionContainer.CreateProxy());
-
- Direction.BindValueChanged(dir =>
- {
- hitTargetContainer.Padding = new MarginPadding
- {
- Top = dir.NewValue == ScrollingDirection.Up ? ManiaStage.HIT_TARGET_POSITION : 0,
- Bottom = dir.NewValue == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0,
- };
-
- explosionContainer.Padding = new MarginPadding
- {
- Top = dir.NewValue == ScrollingDirection.Up ? NotePiece.NOTE_HEIGHT / 2 : 0,
- Bottom = dir.NewValue == ScrollingDirection.Down ? NotePiece.NOTE_HEIGHT / 2 : 0
- };
-
- keyArea.Anchor = keyArea.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
- }, true);
+ TopLevelContainer.Add(hitObjectArea.Explosions.CreateProxy());
}
public override Axes RelativeSizeAxes => Axes.Y;
- private bool isSpecial;
+ public ColumnType ColumnType { get; set; }
- public bool IsSpecial
- {
- get => isSpecial;
- set
- {
- if (isSpecial == value)
- return;
+ public bool IsSpecial => ColumnType == ColumnType.Special;
- isSpecial = value;
-
- Width = isSpecial ? special_column_width : COLUMN_WIDTH;
- }
- }
-
- private Color4 accentColour;
-
- public Color4 AccentColour
- {
- get => accentColour;
- set
- {
- if (accentColour == value)
- return;
-
- accentColour = value;
-
- background.AccentColour = value;
- keyArea.AccentColour = value;
- hitObjectArea.AccentColour = value;
- }
- }
+ public Color4 AccentColour { get; set; }
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
@@ -168,11 +106,15 @@ namespace osu.Game.Rulesets.Mania.UI
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value)
return;
- explosionContainer.Add(new HitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick)
+ var explosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, Index), _ =>
+ new DefaultHitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick))
{
- Anchor = Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre,
- Origin = Anchor.Centre
- });
+ RelativeSizeAxes = Axes.Both
+ };
+
+ hitObjectArea.Explosions.Add(explosion);
+
+ explosion.Delay(200).Expire(true);
}
public bool OnPressed(ManiaAction action)
@@ -197,6 +139,6 @@ namespace osu.Game.Rulesets.Mania.UI
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border
- => DrawRectangle.Inflate(new Vector2(ManiaStage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos));
+ => DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos));
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
index 90e78c3899..cb79bf7f43 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
@@ -1,145 +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.Allocation;
-using osu.Framework.Bindables;
-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.Game.Graphics;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
-using osuTK.Graphics;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.UI.Components
{
- public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
+ public class ColumnHitObjectArea : HitObjectArea
{
- private readonly IBindable direction = new Bindable();
-
+ public readonly Container Explosions;
private readonly Drawable hitTarget;
- public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
+ public ColumnHitObjectArea(int columnIndex, HitObjectContainer hitObjectContainer)
+ : base(hitObjectContainer)
{
- InternalChildren = new[]
+ AddRangeInternal(new[]
{
- hitTarget = new DefaultHitTarget
+ hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget, columnIndex), _ => new DefaultHitTarget())
{
RelativeSizeAxes = Axes.X,
+ Depth = 1
},
- hitObjectContainer
- };
- }
-
- [BackgroundDependencyLoader]
- private void load(IScrollingInfo scrollingInfo)
- {
- direction.BindTo(scrollingInfo.Direction);
- direction.BindValueChanged(dir =>
- {
- Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
-
- hitTarget.Anchor = hitTarget.Origin = anchor;
- }, true);
- }
-
- private Color4 accentColour;
-
- public Color4 AccentColour
- {
- get => accentColour;
- set
- {
- if (accentColour == value)
- return;
-
- accentColour = value;
-
- if (hitTarget is IHasAccentColour colouredHitTarget)
- colouredHitTarget.AccentColour = accentColour;
- }
- }
-
- private class DefaultHitTarget : CompositeDrawable, IHasAccentColour
- {
- private const float hit_target_bar_height = 2;
-
- private readonly IBindable direction = new Bindable();
-
- private readonly Container hitTargetLine;
- private readonly Drawable hitTargetBar;
-
- public DefaultHitTarget()
- {
- InternalChildren = new[]
+ Explosions = new Container
{
- hitTargetBar = new Box
- {
- RelativeSizeAxes = Axes.X,
- Height = NotePiece.NOTE_HEIGHT,
- Alpha = 0.6f,
- Colour = Color4.Black
- },
- hitTargetLine = new Container
- {
- RelativeSizeAxes = Axes.X,
- Height = hit_target_bar_height,
- Masking = true,
- Child = new Box { RelativeSizeAxes = Axes.Both }
- },
- };
- }
-
- [BackgroundDependencyLoader]
- private void load(IScrollingInfo scrollingInfo)
- {
- direction.BindTo(scrollingInfo.Direction);
- direction.BindValueChanged(dir =>
- {
- Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
-
- hitTargetBar.Anchor = hitTargetBar.Origin = anchor;
- hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
- }, true);
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- updateColours();
- }
-
- private Color4 accentColour;
-
- public Color4 AccentColour
- {
- get => accentColour;
- set
- {
- if (accentColour == value)
- return;
-
- accentColour = value;
-
- updateColours();
+ RelativeSizeAxes = Axes.Both,
+ Depth = -1,
}
- }
+ });
+ }
- private void updateColours()
- {
- if (!IsLoaded)
- return;
+ protected override void UpdateHitPosition()
+ {
+ base.UpdateHitPosition();
- hitTargetLine.EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Glow,
- Radius = 5,
- Colour = accentColour.Opacity(0.5f),
- };
- }
+ if (Direction.Value == ScrollingDirection.Up)
+ hitTarget.Anchor = hitTarget.Origin = Anchor.TopLeft;
+ else
+ hitTarget.Anchor = hitTarget.Origin = Anchor.BottomLeft;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
deleted file mode 100644
index 60fc2713b3..0000000000
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
+++ /dev/null
@@ -1,124 +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.Extensions.Color4Extensions;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Colour;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Effects;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.Bindings;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.UI.Scrolling;
-using osuTK;
-using osuTK.Graphics;
-
-namespace osu.Game.Rulesets.Mania.UI.Components
-{
- public class ColumnKeyArea : CompositeDrawable, IKeyBindingHandler, IHasAccentColour
- {
- private const float key_icon_size = 10;
- private const float key_icon_corner_radius = 3;
-
- private readonly IBindable action = new Bindable();
- private readonly IBindable direction = new Bindable();
-
- private Container keyIcon;
-
- [BackgroundDependencyLoader]
- private void load(IBindable action, IScrollingInfo scrollingInfo)
- {
- this.action.BindTo(action);
-
- Drawable gradient;
-
- InternalChildren = new[]
- {
- gradient = new Box
- {
- Name = "Key gradient",
- RelativeSizeAxes = Axes.Both,
- Alpha = 0.5f
- },
- keyIcon = new Container
- {
- Name = "Key icon",
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Size = new Vector2(key_icon_size),
- Masking = true,
- CornerRadius = key_icon_corner_radius,
- BorderThickness = 2,
- BorderColour = Color4.White, // Not true
- Children = new[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Alpha = 0,
- AlwaysPresent = true
- }
- }
- }
- };
-
- direction.BindTo(scrollingInfo.Direction);
- direction.BindValueChanged(dir =>
- {
- gradient.Colour = ColourInfo.GradientVertical(
- dir.NewValue == ScrollingDirection.Up ? Color4.Black : Color4.Black.Opacity(0),
- dir.NewValue == ScrollingDirection.Up ? Color4.Black.Opacity(0) : Color4.Black);
- }, true);
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- updateColours();
- }
-
- private Color4 accentColour;
-
- public Color4 AccentColour
- {
- get => accentColour;
- set
- {
- if (accentColour == value)
- return;
-
- accentColour = value;
-
- updateColours();
- }
- }
-
- private void updateColours()
- {
- if (!IsLoaded)
- return;
-
- keyIcon.EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Glow,
- Radius = 5,
- Colour = accentColour.Opacity(0.5f),
- };
- }
-
- public bool OnPressed(ManiaAction action)
- {
- if (action == this.action.Value)
- keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint);
- return false;
- }
-
- public void OnReleased(ManiaAction action)
- {
- if (action == this.action.Value)
- keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs
new file mode 100644
index 0000000000..4b4bc157d5
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.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.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.UI.Scrolling;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.UI.Components
+{
+ public class DefaultColumnBackground : CompositeDrawable, IKeyBindingHandler
+ {
+ private readonly IBindable direction = new Bindable();
+
+ private Color4 brightColour;
+ private Color4 dimColour;
+
+ private Box background;
+ private Box backgroundOverlay;
+
+ [Resolved]
+ private Column column { get; set; }
+
+ public DefaultColumnBackground()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(IScrollingInfo scrollingInfo)
+ {
+ InternalChildren = new[]
+ {
+ background = new Box
+ {
+ Name = "Background",
+ RelativeSizeAxes = Axes.Both,
+ },
+ backgroundOverlay = new Box
+ {
+ Name = "Background Gradient Overlay",
+ RelativeSizeAxes = Axes.Both,
+ Height = 0.5f,
+ Blending = BlendingParameters.Additive,
+ Alpha = 0
+ }
+ };
+
+ background.Colour = column.AccentColour.Darken(5);
+ brightColour = column.AccentColour.Opacity(0.6f);
+ dimColour = column.AccentColour.Opacity(0);
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.TopLeft;
+ backgroundOverlay.Colour = ColourInfo.GradientVertical(brightColour, dimColour);
+ }
+ else
+ {
+ backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.BottomLeft;
+ backgroundOverlay.Colour = ColourInfo.GradientVertical(dimColour, brightColour);
+ }
+ }
+
+ public bool OnPressed(ManiaAction action)
+ {
+ if (action == column.Action.Value)
+ backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
+ return false;
+ }
+
+ public void OnReleased(ManiaAction action)
+ {
+ if (action == column.Action.Value)
+ backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs
new file mode 100644
index 0000000000..e0b099ab9b
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs
@@ -0,0 +1,80 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using 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.Effects;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.UI.Scrolling;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.UI.Components
+{
+ public class DefaultHitTarget : CompositeDrawable
+ {
+ private const float hit_target_bar_height = 2;
+
+ private readonly IBindable direction = new Bindable();
+
+ private Container hitTargetLine;
+ private Drawable hitTargetBar;
+
+ [Resolved]
+ private Column column { get; set; }
+
+ public DefaultHitTarget()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(IScrollingInfo scrollingInfo)
+ {
+ InternalChildren = new[]
+ {
+ hitTargetBar = new Box
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = DefaultNotePiece.NOTE_HEIGHT,
+ Alpha = 0.6f,
+ Colour = Color4.Black
+ },
+ hitTargetLine = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = hit_target_bar_height,
+ Masking = true,
+ Child = new Box { RelativeSizeAxes = Axes.Both }
+ },
+ };
+
+ hitTargetLine.EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Radius = 5,
+ Colour = column.AccentColour.Opacity(0.5f),
+ };
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ hitTargetBar.Anchor = hitTargetBar.Origin = Anchor.TopLeft;
+ hitTargetLine.Anchor = hitTargetLine.Origin = Anchor.TopLeft;
+ }
+ else
+ {
+ hitTargetBar.Anchor = hitTargetBar.Origin = Anchor.BottomLeft;
+ hitTargetLine.Anchor = hitTargetLine.Origin = Anchor.BottomLeft;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs
new file mode 100644
index 0000000000..47cb9bd45a
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs
@@ -0,0 +1,117 @@
+// 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.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.UI.Scrolling;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.UI.Components
+{
+ public class DefaultKeyArea : CompositeDrawable, IKeyBindingHandler
+ {
+ private const float key_icon_size = 10;
+ private const float key_icon_corner_radius = 3;
+
+ private readonly IBindable direction = new Bindable();
+
+ private Container directionContainer;
+ private Container keyIcon;
+ private Drawable gradient;
+
+ [Resolved]
+ private Column column { get; set; }
+
+ public DefaultKeyArea()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(IScrollingInfo scrollingInfo)
+ {
+ InternalChild = directionContainer = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = Stage.HIT_TARGET_POSITION,
+ Children = new[]
+ {
+ gradient = new Box
+ {
+ Name = "Key gradient",
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.5f
+ },
+ keyIcon = new Container
+ {
+ Name = "Key icon",
+ Size = new Vector2(key_icon_size),
+ Origin = Anchor.Centre,
+ Masking = true,
+ CornerRadius = key_icon_corner_radius,
+ BorderThickness = 2,
+ BorderColour = Color4.White, // Not true
+ Children = new[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true
+ }
+ }
+ }
+ }
+ };
+
+ keyIcon.EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Radius = 5,
+ Colour = column.AccentColour.Opacity(0.5f),
+ };
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ keyIcon.Anchor = Anchor.BottomCentre;
+ keyIcon.Y = -20;
+ directionContainer.Anchor = directionContainer.Origin = Anchor.TopLeft;
+ gradient.Colour = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0));
+ }
+ else
+ {
+ keyIcon.Anchor = Anchor.TopCentre;
+ keyIcon.Y = 20;
+ directionContainer.Anchor = directionContainer.Origin = Anchor.BottomLeft;
+ gradient.Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Color4.Black);
+ }
+ }
+
+ public bool OnPressed(ManiaAction action)
+ {
+ if (action == column.Action.Value)
+ keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint);
+ return false;
+ }
+
+ public void OnReleased(ManiaAction action)
+ {
+ if (action == column.Action.Value)
+ keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultStageBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultStageBackground.cs
new file mode 100644
index 0000000000..f5b542d085
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultStageBackground.cs
@@ -0,0 +1,30 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.UI.Components
+{
+ public class DefaultStageBackground : CompositeDrawable
+ {
+ public DefaultStageBackground()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ InternalChild = new Box
+ {
+ Name = "Background",
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black
+ };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs
new file mode 100644
index 0000000000..ba5281a1a2
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs
@@ -0,0 +1,55 @@
+// 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.Game.Rulesets.Mania.Skinning;
+using osu.Game.Rulesets.UI;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Mania.UI.Components
+{
+ public class HitObjectArea : SkinReloadableDrawable
+ {
+ protected readonly IBindable Direction = new Bindable();
+
+ public HitObjectArea(HitObjectContainer hitObjectContainer)
+ {
+ InternalChildren = new[]
+ {
+ hitObjectContainer,
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(IScrollingInfo scrollingInfo)
+ {
+ Direction.BindTo(scrollingInfo.Direction);
+ Direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ protected override void SkinChanged(ISkinSource skin, bool allowFallback)
+ {
+ base.SkinChanged(skin, allowFallback);
+ UpdateHitPosition();
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ UpdateHitPosition();
+ }
+
+ protected virtual void UpdateHitPosition()
+ {
+ float hitPosition = CurrentSkin.GetConfig(
+ new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
+ ?? Stage.HIT_TARGET_POSITION;
+
+ Padding = Direction.Value == ScrollingDirection.Up
+ ? new MarginPadding { Top = hitPosition }
+ : new MarginPadding { Bottom = hitPosition };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs
similarity index 80%
rename from osu.Game.Rulesets.Mania/UI/HitExplosion.cs
rename to osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs
index 35de47e208..7a047ed121 100644
--- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
+++ b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs
@@ -1,28 +1,35 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Utils;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.UI.Scrolling;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI
{
- internal class HitExplosion : CompositeDrawable
+ public class DefaultHitExplosion : CompositeDrawable
{
public override bool RemoveWhenNotAlive => true;
+ private readonly IBindable direction = new Bindable();
+
private readonly CircularContainer largeFaint;
private readonly CircularContainer mainGlow1;
- public HitExplosion(Color4 objectColour, bool isSmall = false)
+ public DefaultHitExplosion(Color4 objectColour, bool isSmall = false)
{
+ Origin = Anchor.Centre;
+
RelativeSizeAxes = Axes.X;
- Height = NotePiece.NOTE_HEIGHT;
+ Height = DefaultNotePiece.NOTE_HEIGHT;
// scale roughly in-line with visual appearance of notes
Scale = new Vector2(1f, 0.6f);
@@ -109,6 +116,13 @@ namespace osu.Game.Rulesets.Mania.UI
};
}
+ [BackgroundDependencyLoader]
+ private void load(IScrollingInfo scrollingInfo)
+ {
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
protected override void LoadComplete()
{
const double duration = 200;
@@ -122,7 +136,20 @@ namespace osu.Game.Rulesets.Mania.UI
mainGlow1.ScaleTo(1.4f, duration, Easing.OutQuint);
this.FadeOut(duration, Easing.Out);
- Expire(true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ Anchor = Anchor.TopCentre;
+ Y = DefaultNotePiece.NOTE_HEIGHT / 2;
+ }
+ else
+ {
+ Anchor = Anchor.BottomCentre;
+ Y = -DefaultNotePiece.NOTE_HEIGHT / 2;
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
index e5ec054fa7..14cad39b04 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
@@ -5,8 +5,10 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Beatmaps;
@@ -25,6 +27,16 @@ namespace osu.Game.Rulesets.Mania.UI
{
public class DrawableManiaRuleset : DrawableScrollingRuleset
{
+ ///
+ /// The minimum time range. This occurs at a of 40.
+ ///
+ public const double MIN_TIME_RANGE = 150;
+
+ ///
+ /// The maximum time range. This occurs at a of 1.
+ ///
+ public const double MAX_TIME_RANGE = 6000;
+
protected new ManiaPlayfield Playfield => (ManiaPlayfield)base.Playfield;
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
@@ -46,6 +58,19 @@ namespace osu.Game.Rulesets.Mania.UI
[BackgroundDependencyLoader]
private void load()
{
+ bool isForCurrentRuleset = Beatmap.BeatmapInfo.Ruleset.Equals(Ruleset.RulesetInfo);
+
+ foreach (var p in ControlPoints)
+ {
+ // Mania doesn't care about global velocity
+ p.Velocity = 1;
+ p.BaseBeatLength *= Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier;
+
+ // For non-mania beatmap, speed changes should only happen through timing points
+ if (!isForCurrentRuleset)
+ p.DifficultyPoint = new DifficultyControlPoint();
+ }
+
BarLines.ForEach(Playfield.Add);
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
@@ -54,6 +79,17 @@ namespace osu.Game.Rulesets.Mania.UI
Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange);
}
+ protected override void AdjustScrollSpeed(int amount)
+ {
+ this.TransformTo(nameof(relativeTimeRange), relativeTimeRange + amount, 200, Easing.OutQuint);
+ }
+
+ private double relativeTimeRange
+ {
+ get => MAX_TIME_RANGE / TimeRange.Value;
+ set => TimeRange.Value = MAX_TIME_RANGE / value;
+ }
+
///
/// Retrieves the column that intersects a screen-space position.
///
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index 08f6049782..c2eb48b774 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfield : ScrollingPlayfield
{
- private readonly List stages = new List();
+ private readonly List stages = new List();
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => stages.Any(s => s.ReceivePositionalInputAt(screenSpacePos));
@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.UI
for (int i = 0; i < stageDefinitions.Count; i++)
{
- var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
+ var newStage = new Stage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
playfieldGrid.Content[0][i] = newStage;
@@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Mania.UI
///
public int TotalColumns => stages.Sum(s => s.Columns.Count);
- private ManiaStage getStageByColumn(int column)
+ private Stage getStageByColumn(int column)
{
int sum = 0;
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfieldAdjustmentContainer.cs
index d893a3fdde..30e0aafb7d 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfieldAdjustmentContainer.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfieldAdjustmentContainer.cs
@@ -3,7 +3,6 @@
using osu.Framework.Graphics;
using osu.Game.Rulesets.UI;
-using osuTK;
namespace osu.Game.Rulesets.Mania.UI
{
@@ -13,8 +12,6 @@ namespace osu.Game.Rulesets.Mania.UI
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
-
- Size = new Vector2(1, 0.8f);
}
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs
similarity index 62%
rename from osu.Game.Rulesets.Mania/UI/ManiaStage.cs
rename to osu.Game.Rulesets.Mania/UI/Stage.cs
index bfe9f1085b..faa04dea97 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
+++ b/osu.Game.Rulesets.Mania/UI/Stage.cs
@@ -1,20 +1,21 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Skinning;
+using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
@@ -23,30 +24,33 @@ namespace osu.Game.Rulesets.Mania.UI
///
/// A collection of s.
///
- public class ManiaStage : ScrollingPlayfield
+ public class Stage : ScrollingPlayfield
{
public const float COLUMN_SPACING = 1;
- public const float HIT_TARGET_POSITION = 50;
+ public const float HIT_TARGET_POSITION = 110;
public IReadOnlyList Columns => columnFlow.Children;
private readonly FillFlowContainer columnFlow;
- private readonly Container barLineContainer;
-
public Container Judgements => judgements;
private readonly JudgementContainer judgements;
+ private readonly Drawable barLineContainer;
private readonly Container topLevelContainer;
- private List normalColumnColours = new List();
- private Color4 specialColumnColour;
+ private readonly Dictionary columnColours = new Dictionary
+ {
+ { ColumnType.Even, new Color4(6, 84, 0, 255) },
+ { ColumnType.Odd, new Color4(94, 0, 57, 255) },
+ { ColumnType.Special, new Color4(0, 48, 63, 255) }
+ };
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Columns.Any(c => c.ReceivePositionalInputAt(screenSpacePos));
private readonly int firstColumnIndex;
- public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
+ public Stage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
{
this.firstColumnIndex = firstColumnIndex;
@@ -67,31 +71,17 @@ namespace osu.Game.Rulesets.Mania.UI
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
- new Container
+ new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground), _ => new DefaultStageBackground())
{
- Name = "Columns mask",
+ RelativeSizeAxes = Axes.Both
+ },
+ columnFlow = new FillFlowContainer
+ {
+ Name = "Columns",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
- Masking = true,
- CornerRadius = 5,
- Children = new Drawable[]
- {
- new Box
- {
- Name = "Background",
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black
- },
- columnFlow = new FillFlowContainer
- {
- Name = "Columns",
- RelativeSizeAxes = Axes.Y,
- AutoSizeAxes = Axes.X,
- Direction = FillDirection.Horizontal,
- Padding = new MarginPadding { Left = COLUMN_SPACING, Right = COLUMN_SPACING },
- Spacing = new Vector2(COLUMN_SPACING, 0)
- },
- }
+ Direction = FillDirection.Horizontal,
+ Padding = new MarginPadding { Left = COLUMN_SPACING, Right = COLUMN_SPACING },
},
new Container
{
@@ -102,15 +92,18 @@ namespace osu.Game.Rulesets.Mania.UI
Width = 1366, // Bar lines should only be masked on the vertical axis
BypassAutoSizeAxes = Axes.Both,
Masking = true,
- Child = barLineContainer = new Container
+ Child = barLineContainer = new HitObjectArea(HitObjectContainer)
{
Name = "Bar lines",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
- Child = HitObjectContainer
}
},
+ new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null)
+ {
+ RelativeSizeAxes = Axes.Both
+ },
judgements = new JudgementContainer
{
Anchor = Anchor.TopCentre,
@@ -125,24 +118,52 @@ namespace osu.Game.Rulesets.Mania.UI
for (int i = 0; i < definition.Columns; i++)
{
- var isSpecial = definition.IsSpecialColumn(i);
+ var columnType = definition.GetTypeOfColumn(i);
var column = new Column(firstColumnIndex + i)
{
- IsSpecial = isSpecial,
- Action = { Value = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ }
+ ColumnType = columnType,
+ AccentColour = columnColours[columnType],
+ Action = { Value = columnType == ColumnType.Special ? specialColumnStartAction++ : normalColumnStartAction++ }
};
AddColumn(column);
}
+ }
- Direction.BindValueChanged(dir =>
+ private ISkin currentSkin;
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin)
+ {
+ currentSkin = skin;
+ skin.SourceChanged += onSkinChanged;
+
+ onSkinChanged();
+ }
+
+ private void onSkinChanged()
+ {
+ foreach (var col in columnFlow)
{
- barLineContainer.Padding = new MarginPadding
+ if (col.Index > 0)
{
- Top = dir.NewValue == ScrollingDirection.Up ? HIT_TARGET_POSITION : 0,
- Bottom = dir.NewValue == ScrollingDirection.Down ? HIT_TARGET_POSITION : 0,
- };
- }, true);
+ float spacing = currentSkin.GetConfig(
+ new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, col.Index - 1))
+ ?.Value ?? COLUMN_SPACING;
+
+ col.Margin = new MarginPadding { Left = spacing };
+ }
+
+ float? width = currentSkin.GetConfig(
+ new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, col.Index))
+ ?.Value;
+
+ if (width == null)
+ // only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration)
+ col.Width = col.IsSpecial ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH;
+ else
+ col.Width = width.Value;
+ }
}
public void AddColumn(Column c)
@@ -195,38 +216,6 @@ namespace osu.Game.Rulesets.Mania.UI
});
}
- [BackgroundDependencyLoader]
- private void load()
- {
- normalColumnColours = new List
- {
- new Color4(94, 0, 57, 255),
- new Color4(6, 84, 0, 255)
- };
-
- specialColumnColour = new Color4(0, 48, 63, 255);
-
- // Set the special column + colour + key
- foreach (var column in Columns)
- {
- if (!column.IsSpecial)
- continue;
-
- column.AccentColour = specialColumnColour;
- }
-
- var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
-
- // We'll set the colours of the non-special columns in a separate loop, because the non-special
- // column colours are mirrored across their centre and special styles mess with this
- for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
- {
- Color4 colour = normalColumnColours[i % normalColumnColours.Count];
- nonSpecialColumns[i].AccentColour = colour;
- nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
- }
- }
-
protected override void Update()
{
// Due to masking differences, it is not possible to get the width of the columns container automatically
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs
new file mode 100644
index 0000000000..90ebbd9f04
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs
@@ -0,0 +1,21 @@
+// 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 osu.Game.Rulesets.Osu.Skinning;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public abstract class OsuSkinnableTestScene : SkinnableTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(OsuRuleset),
+ typeof(OsuLegacySkinTransformer),
+ };
+
+ protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/sliderstartcircle@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/sliderstartcircle@2x.png
new file mode 100644
index 0000000000..4d630443cd
Binary files /dev/null and b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/sliderstartcircle@2x.png differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/sliderstartcircleoverlay@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/sliderstartcircleoverlay@2x.png
new file mode 100644
index 0000000000..a824784942
Binary files /dev/null and b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/sliderstartcircleoverlay@2x.png differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/sliderb0.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/sliderb0.png
new file mode 100644
index 0000000000..316d52c685
Binary files /dev/null and b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/sliderb0.png differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/sliderb0@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/sliderb0@2x.png
new file mode 100644
index 0000000000..e6f6b3c239
Binary files /dev/null and b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/sliderb0@2x.png differ
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
index 02d4406809..f867630df6 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
@@ -10,17 +10,16 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
- public class TestSceneDrawableJudgement : SkinnableTestScene
+ public class TestSceneDrawableJudgement : OsuSkinnableTestScene
{
- public override IReadOnlyList RequiredTypes => new[]
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[]
{
typeof(DrawableJudgement),
typeof(DrawableOsuJudgement)
- };
+ }).ToList();
public TestSceneDrawableJudgement()
{
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
index 7b96e2ec6a..22dacc6f5e 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
@@ -3,26 +3,32 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing.Input;
using osu.Game.Configuration;
+using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.UI.Cursor;
+using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
-using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
- public class TestSceneGameplayCursor : SkinnableTestScene
+ public class TestSceneGameplayCursor : OsuSkinnableTestScene
{
- public override IReadOnlyList RequiredTypes => new[]
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[]
{
+ typeof(GameplayCursorContainer),
typeof(OsuCursorContainer),
+ typeof(OsuCursor),
+ typeof(LegacyCursor),
+ typeof(LegacyCursorTrail),
typeof(CursorTrail)
- };
+ }).ToList();
[Cached]
private GameplayBeatmap gameplayBeatmap;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
index ae5a28217c..e117729f01 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
@@ -14,12 +14,11 @@ using osu.Game.Rulesets.Mods;
using System.Linq;
using NUnit.Framework;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
- public class TestSceneHitCircle : SkinnableTestScene
+ public class TestSceneHitCircle : OsuSkinnableTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index a201364de4..eb6130c8a6 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -22,12 +22,11 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
-using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
- public class TestSceneSlider : SkinnableTestScene
+ public class TestSceneSlider : OsuSkinnableTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs
new file mode 100644
index 0000000000..f5b20fd1c5
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs
@@ -0,0 +1,253 @@
+// 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 Humanizer;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
+using osu.Framework.Timing;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Objects;
+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.Storyboards;
+using osuTK;
+using static osu.Game.Tests.Visual.OsuTestScene.ClockBackedTestWorkingBeatmap;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [TestFixture]
+ public class TestSceneSliderSnaking : TestSceneOsuPlayer
+ {
+ [Resolved]
+ private AudioManager audioManager { get; set; }
+
+ private TrackVirtualManual track;
+
+ protected override bool Autoplay => autoplay;
+ private bool autoplay;
+
+ private readonly BindableBool snakingIn = new BindableBool();
+ private readonly BindableBool snakingOut = new BindableBool();
+
+ private const double duration_of_span = 3605;
+ private const double fade_in_modifier = -1200;
+
+ protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
+ {
+ var working = new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
+ track = (TrackVirtualManual)working.Track;
+ return working;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetConfigCache configCache)
+ {
+ var config = (OsuRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
+ config.BindWith(OsuRulesetSetting.SnakingInSliders, snakingIn);
+ config.BindWith(OsuRulesetSetting.SnakingOutSliders, snakingOut);
+ }
+
+ private DrawableSlider slider;
+
+ [SetUpSteps]
+ public override void SetUpSteps() { }
+
+ [TestCase(0)]
+ [TestCase(1)]
+ [TestCase(2)]
+ public void TestSnakingEnabled(int sliderIndex)
+ {
+ AddStep("enable autoplay", () => autoplay = true);
+ base.SetUpSteps();
+ AddUntilStep("wait for track to start running", () => track.IsRunning);
+
+ double startTime = hitObjects[sliderIndex].StartTime;
+ retrieveDrawableSlider(sliderIndex);
+ setSnaking(true);
+
+ ensureSnakingIn(startTime + fade_in_modifier);
+
+ for (int i = 0; i < sliderIndex; i++)
+ {
+ // non-final repeats should not snake out
+ ensureNoSnakingOut(startTime, i);
+ }
+
+ // final repeat should snake out
+ ensureSnakingOut(startTime, sliderIndex);
+ }
+
+ [TestCase(0)]
+ [TestCase(1)]
+ [TestCase(2)]
+ public void TestSnakingDisabled(int sliderIndex)
+ {
+ AddStep("have autoplay", () => autoplay = true);
+ base.SetUpSteps();
+ AddUntilStep("wait for track to start running", () => track.IsRunning);
+
+ double startTime = hitObjects[sliderIndex].StartTime;
+ retrieveDrawableSlider(sliderIndex);
+ setSnaking(false);
+
+ ensureNoSnakingIn(startTime + fade_in_modifier);
+
+ for (int i = 0; i <= sliderIndex; i++)
+ {
+ // no snaking out ever, including final repeat
+ ensureNoSnakingOut(startTime, i);
+ }
+ }
+
+ [Test]
+ public void TestRepeatArrowDoesNotMoveWhenHit()
+ {
+ AddStep("enable autoplay", () => autoplay = true);
+ setSnaking(true);
+ base.SetUpSteps();
+
+ // repeat might have a chance to update its position depending on where in the frame its hit,
+ // so some leniency is allowed here instead of checking strict equality
+ checkPositionChange(16600, sliderRepeat, positionAlmostSame);
+ }
+
+ [Test]
+ public void TestRepeatArrowMovesWhenNotHit()
+ {
+ AddStep("disable autoplay", () => autoplay = false);
+ setSnaking(true);
+ base.SetUpSteps();
+
+ checkPositionChange(16600, sliderRepeat, positionDecreased);
+ }
+
+ private void retrieveDrawableSlider(int index) => AddStep($"retrieve {(index + 1).ToOrdinalWords()} slider", () =>
+ {
+ slider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(index);
+ });
+
+ private void ensureSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionIncreased);
+ private void ensureNoSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionRemainsSame);
+
+ private void ensureSnakingOut(double startTime, int repeatIndex)
+ {
+ var repeatTime = timeAtRepeat(startTime, repeatIndex);
+
+ if (repeatIndex % 2 == 0)
+ checkPositionChange(repeatTime, sliderStart, positionIncreased);
+ else
+ checkPositionChange(repeatTime, sliderEnd, positionDecreased);
+ }
+
+ private void ensureNoSnakingOut(double startTime, int repeatIndex) =>
+ checkPositionChange(timeAtRepeat(startTime, repeatIndex), positionAtRepeat(repeatIndex), positionRemainsSame);
+
+ private double timeAtRepeat(double startTime, int repeatIndex) => startTime + 100 + duration_of_span * repeatIndex;
+ private Func positionAtRepeat(int repeatIndex) => repeatIndex % 2 == 0 ? (Func)sliderStart : sliderEnd;
+
+ private List sliderCurve => ((PlaySliderBody)slider.Body.Drawable).CurrentCurve;
+ private Vector2 sliderStart() => sliderCurve.First();
+ private Vector2 sliderEnd() => sliderCurve.Last();
+
+ private Vector2 sliderRepeat()
+ {
+ var drawable = Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(1);
+ var repeat = drawable.ChildrenOfType>().First().Children.First();
+ return repeat.Position;
+ }
+
+ private bool positionRemainsSame(Vector2 previous, Vector2 current) => previous == current;
+ private bool positionIncreased(Vector2 previous, Vector2 current) => current.X > previous.X && current.Y > previous.Y;
+ private bool positionDecreased(Vector2 previous, Vector2 current) => current.X < previous.X && current.Y < previous.Y;
+ private bool positionAlmostSame(Vector2 previous, Vector2 current) => Precision.AlmostEquals(previous, current, 1);
+
+ private void checkPositionChange(double startTime, Func positionToCheck, Func positionAssertion)
+ {
+ Vector2 previousPosition = Vector2.Zero;
+
+ string positionDescription = positionToCheck.Method.Name.Humanize(LetterCasing.LowerCase);
+ string assertionDescription = positionAssertion.Method.Name.Humanize(LetterCasing.LowerCase);
+
+ addSeekStep(startTime);
+ AddStep($"save {positionDescription} position", () => previousPosition = positionToCheck.Invoke());
+ addSeekStep(startTime + 100);
+ AddAssert($"{positionDescription} {assertionDescription}", () =>
+ {
+ var currentPosition = positionToCheck.Invoke();
+ return positionAssertion.Invoke(previousPosition, currentPosition);
+ });
+ }
+
+ private void setSnaking(bool value)
+ {
+ AddStep($"{(value ? "enable" : "disable")} snaking", () =>
+ {
+ snakingIn.Value = value;
+ snakingOut.Value = value;
+ });
+ }
+
+ private void addSeekStep(double time)
+ {
+ AddStep($"seek to {time}", () => track.Seek(time));
+
+ AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
+ }
+
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap
+ {
+ HitObjects = hitObjects
+ };
+
+ private readonly List hitObjects = new List
+ {
+ new Slider
+ {
+ StartTime = 3000,
+ Position = new Vector2(100, 100),
+ Path = new SliderPath(PathType.PerfectCurve, new[]
+ {
+ Vector2.Zero,
+ new Vector2(300, 200)
+ }),
+ },
+ new Slider
+ {
+ StartTime = 13000,
+ Position = new Vector2(100, 100),
+ Path = new SliderPath(PathType.PerfectCurve, new[]
+ {
+ Vector2.Zero,
+ new Vector2(300, 200)
+ }),
+ RepeatCount = 1,
+ },
+ new Slider
+ {
+ StartTime = 23000,
+ Position = new Vector2(100, 100),
+ Path = new SliderPath(PathType.PerfectCurve, new[]
+ {
+ Vector2.Zero,
+ new Vector2(300, 200)
+ }),
+ RepeatCount = 2,
+ },
+ new HitCircle
+ {
+ StartTime = 199999,
+ }
+ };
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
index c18b3b0ff3..001100d3ce 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
@@ -15,6 +15,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose;
using osuTK;
using osuTK.Input;
@@ -34,6 +35,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
[Resolved(CanBeNull = true)]
private IPlacementHandler placementHandler { get; set; }
+ [Resolved(CanBeNull = true)]
+ private EditorBeatmap editorBeatmap { get; set; }
+
public SliderSelectionBlueprint(DrawableSlider slider)
: base(slider)
{
@@ -162,7 +166,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private void updatePath()
{
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
- UpdateHitObject();
+ editorBeatmap?.UpdateHitObject(HitObject);
}
public override MenuItem[] ContextMenuItems => new MenuItem[]
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
index 6286c80d7c..9b0759d9d2 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
@@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Mods
void handleHitCircle(DrawableHitCircle circle)
{
- if (!circle.IsHovered)
+ if (!circle.HitArea.IsHovered)
return;
Debug.Assert(circle.HitObject.HitWindows != null);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
index 7e530ca047..a981648444 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
///
/// A single follow point positioned between two adjacent s.
///
- public class FollowPoint : Container
+ public class FollowPoint : Container, IAnimationTimeReference
{
private const float width = 8;
@@ -43,7 +43,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Anchor = Anchor.Centre,
Alpha = 0.5f,
}
- }, confineMode: ConfineMode.NoScaling);
+ });
}
+
+ public double AnimationStartTime { get; set; }
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
index d0935e46f7..6f09bbcd57 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
@@ -116,6 +116,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
int point = 0;
+ ClearInternal();
+
for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
{
float fraction = (float)d / distance;
@@ -126,13 +128,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPoint fp;
- if (InternalChildren.Count > point)
- {
- fp = (FollowPoint)InternalChildren[point];
- fp.ClearTransforms();
- }
- else
- AddInternal(fp = new FollowPoint());
+ AddInternal(fp = new FollowPoint());
fp.Position = pointStartPosition;
fp.Rotation = rotation;
@@ -142,6 +138,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
if (firstTransformStartTime == null)
firstTransformStartTime = fadeInTime;
+ fp.AnimationStartTime = fadeInTime;
+
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(osuEnd.TimeFadeIn);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 3ca2714511..5776c64c86 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public readonly SkinnableDrawable CirclePiece;
private readonly Container scaleContainer;
+ protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle;
+
public DrawableHitCircle(HitCircle h)
: base(h)
{
@@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return true;
},
},
- CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.HitCircle), _ => new MainCirclePiece()),
+ CirclePiece = new SkinnableDrawable(new OsuSkinComponent(CirclePieceComponent), _ => new MainCirclePiece()),
ApproachCircle = new ApproachCircle
{
Alpha = 0,
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index b017eacf70..522217a916 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -186,7 +186,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.ApplySkin(skin, allowFallback);
bool allowBallTint = skin.GetConfig(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false;
- Ball.Colour = allowBallTint ? AccentColour.Value : Color4.White;
+ Ball.AccentColour = allowBallTint ? AccentColour.Value : Color4.White;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
index 563282e18f..04f563eeec 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
@@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable positionBindable = new Bindable();
private readonly IBindable pathVersion = new Bindable();
+ protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle;
+
private readonly Slider slider;
public DrawableSliderHead(Slider slider, SliderHeadCircle h)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
index b9cee71ca1..720ffcd51c 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
@@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
- Blending = BlendingParameters.Additive;
Origin = Anchor.Centre;
InternalChild = scaleContainer = new ReverseArrowPiece();
@@ -87,6 +86,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
{
+ // When the repeat is hit, the arrow should fade out on spot rather than following the slider
+ if (IsHit) return;
+
bool isRepeatAtEnd = sliderRepeat.RepeatIndex % 2 == 0;
List curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs
index 35a27bb0a6..1a5195acf8 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs
@@ -8,11 +8,16 @@ 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;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class ReverseArrowPiece : BeatSyncedContainer
{
+ [Resolved]
+ private DrawableHitObject drawableRepeat { get; set; }
+
public ReverseArrowPiece()
{
Divisor = 2;
@@ -21,13 +26,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- Blending = BlendingParameters.Additive;
-
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
+ Blending = BlendingParameters.Additive,
Icon = FontAwesome.Solid.ChevronRight,
Size = new Vector2(0.35f)
})
@@ -37,7 +41,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
};
}
- protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) =>
- Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out);
+ protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
+ {
+ if (!drawableRepeat.IsHit)
+ Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out);
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 5a6dd49c44..395c76a233 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -40,7 +40,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
this.drawableSlider = drawableSlider;
this.slider = slider;
- Blending = BlendingParameters.Additive;
Origin = Anchor.Centre;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
@@ -241,6 +240,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Scale = new Vector2(radius / OsuHitObject.OBJECT_RADIUS),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
+ Blending = BlendingParameters.Additive,
BorderThickness = 10,
BorderColour = Color4.White,
Alpha = 1,
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index a0f5b8fe01..689a7b35ea 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -179,7 +179,7 @@ namespace osu.Game.Rulesets.Osu
public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);
- public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source);
+ public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new OsuLegacySkinTransformer(source);
public int LegacyID => 0;
diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs
index 4ea4220faf..b2cdc8ccbf 100644
--- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs
+++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs
@@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Osu
ApproachCircle,
ReverseArrow,
HitCircleText,
+ SliderHeadHitCircle,
SliderFollowCircle,
SliderBall,
SliderBody,
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs
index 93ae0371df..e7486ef9b0 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs
@@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables;
@@ -18,8 +19,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
public class LegacyMainCirclePiece : CompositeDrawable
{
- public LegacyMainCirclePiece()
+ private readonly string priorityLookup;
+
+ public LegacyMainCirclePiece(string priorityLookup = null)
{
+ this.priorityLookup = priorityLookup;
+
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
}
@@ -39,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
hitCircleSprite = new Sprite
{
- Texture = skin.GetTexture("hitcircle"),
+ Texture = getTextureWithFallback(string.Empty),
Colour = drawableObject.AccentColour.Value,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -51,12 +56,17 @@ namespace osu.Game.Rulesets.Osu.Skinning
}, confineMode: ConfineMode.NoScaling),
new Sprite
{
- Texture = skin.GetTexture("hitcircleoverlay"),
+ Texture = getTextureWithFallback("overlay"),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
};
+ bool overlayAboveNumber = skin.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
+
+ if (!overlayAboveNumber)
+ ChangeInternalChildDepth(hitCircleText, -float.MaxValue);
+
state.BindTo(drawableObject.State);
state.BindValueChanged(updateState, true);
@@ -65,6 +75,16 @@ namespace osu.Game.Rulesets.Osu.Skinning
indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable);
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
+
+ Texture getTextureWithFallback(string name)
+ {
+ Texture tex = null;
+
+ if (!string.IsNullOrEmpty(priorityLookup))
+ tex = skin.GetTexture($"{priorityLookup}{name}");
+
+ return tex ?? skin.GetTexture($"hitcircle{name}");
+ }
}
private void updateState(ValueChangedEvent state)
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
index 81c02199d0..b4ed75d97c 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
@@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
public LegacySliderBall(Drawable animationContent)
{
this.animationContent = animationContent;
+
+ AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
index d6c3f443eb..ba0003b5cd 100644
--- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
switch (osuComponent.Component)
{
case OsuSkinComponents.FollowPoint:
- return this.GetAnimation(component.LookupName, true, false, true);
+ return this.GetAnimation(component.LookupName, true, false, true, startAtCurrentTime: false);
case OsuSkinComponents.SliderFollowCircle:
var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true);
@@ -62,17 +62,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
if (sliderBallContent != null)
- {
- var size = sliderBallContent.Size;
-
- sliderBallContent.RelativeSizeAxes = Axes.Both;
- sliderBallContent.Size = Vector2.One;
-
- return new LegacySliderBall(sliderBallContent)
- {
- Size = size
- };
- }
+ return new LegacySliderBall(sliderBallContent);
return null;
@@ -82,6 +72,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
return null;
+ case OsuSkinComponents.SliderHeadHitCircle:
+ if (hasHitCircle.Value)
+ return new LegacyMainCirclePiece("sliderstartcircle");
+
+ return null;
+
case OsuSkinComponents.HitCircle:
if (hasHitCircle.Value)
return new LegacyMainCirclePiece();
@@ -136,6 +132,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
return SkinUtils.As(new BindableFloat(LEGACY_CIRCLE_RADIUS));
break;
+
+ case OsuSkinConfiguration.HitCircleOverlayAboveNumber:
+ // See https://osu.ppy.sh/help/wiki/Skinning/skin.ini#%5Bgeneral%5D
+ // HitCircleOverlayAboveNumer (with typo) should still be supported for now.
+ return source.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber) ??
+ source.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumer);
}
break;
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
index 5d99960f10..154160fdb5 100644
--- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
@@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
SliderPathRadius,
AllowSliderBallTint,
CursorExpand,
- CursorRotate
+ CursorRotate,
+ HitCircleOverlayAboveNumber,
+ HitCircleOverlayAboveNumer // Some old skins will have this typo
}
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-bar-left@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-bar-left@2x.png
new file mode 100644
index 0000000000..dc3d7f4c70
Binary files /dev/null and b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-bar-left@2x.png differ
diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-drum-inner@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-drum-inner@2x.png
new file mode 100644
index 0000000000..15a89ade1b
Binary files /dev/null and b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-drum-inner@2x.png differ
diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-drum-outer@2x.png b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-drum-outer@2x.png
new file mode 100644
index 0000000000..a01583c6fb
Binary files /dev/null and b/osu.Game.Rulesets.Taiko.Tests/Resources/metrics-skin/taiko-drum-outer@2x.png differ
diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/skin.ini b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/skin.ini
new file mode 100644
index 0000000000..462c2c278e
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/skin.ini
@@ -0,0 +1,5 @@
+[General]
+Name: an old skin
+Author: an old guy
+
+// no version specified means v1
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-bar-left.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-bar-left.png
new file mode 100644
index 0000000000..ad55fd5a96
Binary files /dev/null and b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-bar-left.png differ
diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-drum-inner.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-drum-inner.png
new file mode 100644
index 0000000000..f5c02509fb
Binary files /dev/null and b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-drum-inner.png differ
diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-drum-outer.png b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-drum-outer.png
new file mode 100644
index 0000000000..53905792cb
Binary files /dev/null and b/osu.Game.Rulesets.Taiko.Tests/Resources/old-skin/taiko-drum-outer.png differ
diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs
new file mode 100644
index 0000000000..6db2a6907f
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests/TaikoSkinnableTestScene.cs
@@ -0,0 +1,21 @@
+// 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 osu.Game.Rulesets.Taiko.Skinning;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Taiko.Tests
+{
+ public abstract class TaikoSkinnableTestScene : SkinnableTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(TaikoRuleset),
+ typeof(TaikoLegacySkinTransformer),
+ };
+
+ protected override Ruleset CreateRulesetForSkinProvider() => new TaikoRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs
index 8c1b0c4c62..1928e9f66f 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs
@@ -3,32 +3,31 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Rulesets.Taiko.Audio;
+using osu.Game.Rulesets.Taiko.Skinning;
using osu.Game.Rulesets.Taiko.UI;
-using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
{
[TestFixture]
- public class TestSceneInputDrum : OsuTestScene
+ public class TestSceneInputDrum : TaikoSkinnableTestScene
{
- public override IReadOnlyList RequiredTypes => new[]
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[]
{
typeof(InputDrum),
- typeof(DrumSampleMapping),
- typeof(HitSampleInfo),
- typeof(SampleControlPoint)
- };
+ typeof(LegacyInputDrum),
+ }).ToList();
- public TestSceneInputDrum()
+ [BackgroundDependencyLoader]
+ private void load()
{
- Add(new TaikoInputManager(new RulesetInfo { ID = 1 })
+ SetContents(() => new TaikoInputManager(new RulesetInfo { ID = 1 })
{
RelativeSizeAxes = Axes.Both,
Child = new Container
diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs
new file mode 100644
index 0000000000..c61e35692b
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyInputDrum.cs
@@ -0,0 +1,167 @@
+// 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.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.Taiko.Audio;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Taiko.Skinning
+{
+ ///
+ /// A component of the playfield that captures input and displays input as a drum.
+ ///
+ internal class LegacyInputDrum : Container
+ {
+ private LegacyHalfDrum left;
+ private LegacyHalfDrum right;
+
+ public LegacyInputDrum()
+ {
+ Size = new Vector2(180, 200);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin)
+ {
+ Children = new Drawable[]
+ {
+ new Sprite
+ {
+ Texture = skin.GetTexture("taiko-bar-left")
+ },
+ left = new LegacyHalfDrum(false)
+ {
+ Name = "Left Half",
+ RelativeSizeAxes = Axes.Both,
+ RimAction = TaikoAction.LeftRim,
+ CentreAction = TaikoAction.LeftCentre
+ },
+ right = new LegacyHalfDrum(true)
+ {
+ Name = "Right Half",
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.TopRight,
+ Scale = new Vector2(-1, 1),
+ RimAction = TaikoAction.RightRim,
+ CentreAction = TaikoAction.RightCentre
+ }
+ };
+
+ // this will be used in the future for stable skin alignment. keeping here for reference.
+ const float taiko_bar_y = 0;
+
+ // stable things
+ const float ratio = 1.6f;
+
+ // because the right half is flipped, we need to position using width - position to get the true "topleft" origin position
+ float negativeScaleAdjust = Width / ratio;
+
+ if (skin.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.1m)
+ {
+ left.Centre.Position = new Vector2(0, taiko_bar_y) * ratio;
+ right.Centre.Position = new Vector2(negativeScaleAdjust - 56, taiko_bar_y) * ratio;
+ left.Rim.Position = new Vector2(0, taiko_bar_y) * ratio;
+ right.Rim.Position = new Vector2(negativeScaleAdjust - 56, taiko_bar_y) * ratio;
+ }
+ else
+ {
+ left.Centre.Position = new Vector2(18, taiko_bar_y + 31) * ratio;
+ right.Centre.Position = new Vector2(negativeScaleAdjust - 54, taiko_bar_y + 31) * ratio;
+ left.Rim.Position = new Vector2(8, taiko_bar_y + 23) * ratio;
+ right.Rim.Position = new Vector2(negativeScaleAdjust - 53, taiko_bar_y + 23) * ratio;
+ }
+ }
+
+ ///
+ /// A half-drum. Contains one centre and one rim hit.
+ ///
+ private class LegacyHalfDrum : Container, IKeyBindingHandler
+ {
+ ///
+ /// The key to be used for the rim of the half-drum.
+ ///
+ public TaikoAction RimAction;
+
+ ///
+ /// The key to be used for the centre of the half-drum.
+ ///
+ public TaikoAction CentreAction;
+
+ public readonly Sprite Rim;
+ public readonly Sprite Centre;
+
+ [Resolved]
+ private DrumSampleMapping sampleMappings { get; set; }
+
+ public LegacyHalfDrum(bool flipped)
+ {
+ Masking = true;
+
+ Children = new Drawable[]
+ {
+ Rim = new Sprite
+ {
+ Scale = new Vector2(-1, 1),
+ Origin = flipped ? Anchor.TopLeft : Anchor.TopRight,
+ Alpha = 0,
+ },
+ Centre = new Sprite
+ {
+ Alpha = 0,
+ Origin = flipped ? Anchor.TopRight : Anchor.TopLeft,
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin)
+ {
+ Rim.Texture = skin.GetTexture(@"taiko-drum-outer");
+ Centre.Texture = skin.GetTexture(@"taiko-drum-inner");
+ }
+
+ public bool OnPressed(TaikoAction action)
+ {
+ Drawable target = null;
+ var drumSample = sampleMappings.SampleAt(Time.Current);
+
+ if (action == CentreAction)
+ {
+ target = Centre;
+ drumSample.Centre?.Play();
+ }
+ else if (action == RimAction)
+ {
+ target = Rim;
+ drumSample.Rim?.Play();
+ }
+
+ if (target != null)
+ {
+ const float alpha_amount = 1;
+
+ const float down_time = 80;
+ const float up_time = 50;
+
+ target.Animate(
+ t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time)
+ ).Then(
+ t => t.FadeOut(up_time)
+ );
+ }
+
+ return false;
+ }
+
+ public void OnReleased(TaikoAction action)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs
index 381cd14cd4..78eec94590 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs
@@ -20,7 +20,22 @@ namespace osu.Game.Rulesets.Taiko.Skinning
this.source = source;
}
- public Drawable GetDrawableComponent(ISkinComponent component) => source.GetDrawableComponent(component);
+ public Drawable GetDrawableComponent(ISkinComponent component)
+ {
+ if (!(component is TaikoSkinComponent taikoComponent))
+ return null;
+
+ switch (taikoComponent.Component)
+ {
+ case TaikoSkinComponents.InputDrum:
+ if (GetTexture("taiko-bar-left") != null)
+ return new LegacyInputDrum();
+
+ return null;
+ }
+
+ return source.GetDrawableComponent(component);
+ }
public Texture GetTexture(string componentName) => source.GetTexture(componentName);
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index a6c9a33569..74d9e68ad3 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap, this);
- public override ISkin CreateLegacySkinProvider(ISkinSource source) => new TaikoLegacySkinTransformer(source);
+ public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new TaikoLegacySkinTransformer(source);
public const string SHORT_NAME = "taiko";
diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs
index 04aca534c6..6d4581db80 100644
--- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs
@@ -5,5 +5,6 @@ namespace osu.Game.Rulesets.Taiko
{
public enum TaikoSkinComponents
{
+ InputDrum,
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
index d26ccfe867..422ea2f929 100644
--- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
@@ -12,6 +12,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Audio;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.UI
{
@@ -22,11 +23,12 @@ namespace osu.Game.Rulesets.Taiko.UI
{
private const float middle_split = 0.025f;
- private readonly ControlPointInfo controlPoints;
+ [Cached]
+ private DrumSampleMapping sampleMapping;
public InputDrum(ControlPointInfo controlPoints)
{
- this.controlPoints = controlPoints;
+ sampleMapping = new DrumSampleMapping(controlPoints);
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fit;
@@ -35,35 +37,37 @@ namespace osu.Game.Rulesets.Taiko.UI
[BackgroundDependencyLoader]
private void load()
{
- var sampleMappings = new DrumSampleMapping(controlPoints);
-
- Children = new Drawable[]
+ Child = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new Container
{
- new TaikoHalfDrum(false, sampleMappings)
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- Name = "Left Half",
- Anchor = Anchor.Centre,
- Origin = Anchor.CentreRight,
- RelativeSizeAxes = Axes.Both,
- RelativePositionAxes = Axes.X,
- X = -middle_split / 2,
- RimAction = TaikoAction.LeftRim,
- CentreAction = TaikoAction.LeftCentre
- },
- new TaikoHalfDrum(true, sampleMappings)
- {
- Name = "Right Half",
- Anchor = Anchor.Centre,
- Origin = Anchor.CentreLeft,
- RelativeSizeAxes = Axes.Both,
- RelativePositionAxes = Axes.X,
- X = middle_split / 2,
- RimAction = TaikoAction.RightRim,
- CentreAction = TaikoAction.RightCentre
+ new TaikoHalfDrum(false)
+ {
+ Name = "Left Half",
+ Anchor = Anchor.Centre,
+ Origin = Anchor.CentreRight,
+ RelativeSizeAxes = Axes.Both,
+ RelativePositionAxes = Axes.X,
+ X = -middle_split / 2,
+ RimAction = TaikoAction.LeftRim,
+ CentreAction = TaikoAction.LeftCentre
+ },
+ new TaikoHalfDrum(true)
+ {
+ Name = "Right Half",
+ Anchor = Anchor.Centre,
+ Origin = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.Both,
+ RelativePositionAxes = Axes.X,
+ X = middle_split / 2,
+ RimAction = TaikoAction.RightRim,
+ CentreAction = TaikoAction.RightCentre
+ }
}
- };
+ });
- AddRangeInternal(sampleMappings.Sounds);
+ AddRangeInternal(sampleMapping.Sounds);
}
///
@@ -86,12 +90,11 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Sprite centre;
private readonly Sprite centreHit;
- private readonly DrumSampleMapping sampleMappings;
+ [Resolved]
+ private DrumSampleMapping sampleMappings { get; set; }
- public TaikoHalfDrum(bool flipped, DrumSampleMapping sampleMappings)
+ public TaikoHalfDrum(bool flipped)
{
- this.sampleMappings = sampleMappings;
-
Masking = true;
Children = new Drawable[]
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 63346b8c9d..b034e66616 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -127,6 +127,31 @@ namespace osu.Game.Tests.Beatmaps.Formats
.Assert();
}
+ [Test]
+ public void TestGetJsonDecoder()
+ {
+ Decoder decoder;
+
+ using (var stream = TestResources.OpenResource(normal))
+ using (var sr = new LineBufferedReader(stream))
+ {
+ var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
+
+ using (var memStream = new MemoryStream())
+ using (var memWriter = new StreamWriter(memStream))
+ using (var memReader = new LineBufferedReader(memStream))
+ {
+ memWriter.Write(legacyDecoded.Serialize());
+ memWriter.Flush();
+
+ memStream.Position = 0;
+ decoder = Decoder.GetDecoder(memReader);
+ }
+ }
+
+ Assert.IsInstanceOf(typeof(JsonBeatmapDecoder), decoder);
+ }
+
///
/// Reads a .osu file first with a , serializes the resulting to JSON
/// and then deserializes the result back into a through an .
diff --git a/osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs b/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs
similarity index 79%
rename from osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs
rename to osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs
index 12d729d09f..d367d9f88b 100644
--- a/osu.Game.Tests/Beatmaps/EditorBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/TestSceneEditorBeatmap.cs
@@ -4,15 +4,17 @@
using System.Linq;
using Microsoft.EntityFrameworkCore.Internal;
using NUnit.Framework;
+using osu.Framework.Testing;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
+using osu.Game.Tests.Visual;
namespace osu.Game.Tests.Beatmaps
{
- [TestFixture]
- public class EditorBeatmapTest
+ [HeadlessTest]
+ public class TestSceneEditorBeatmap : EditorClockTestScene
{
///
/// Tests that the addition event is correctly invoked after a hitobject is added.
@@ -55,13 +57,19 @@ namespace osu.Game.Tests.Beatmaps
public void TestInitialHitObjectStartTimeChangeEvent()
{
var hitCircle = new HitCircle();
- var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } });
HitObject changedObject = null;
- editorBeatmap.StartTimeChanged += h => changedObject = h;
- hitCircle.StartTime = 1000;
- Assert.That(changedObject, Is.EqualTo(hitCircle));
+ AddStep("add beatmap", () =>
+ {
+ EditorBeatmap editorBeatmap;
+
+ Child = editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } });
+ editorBeatmap.HitObjectUpdated += h => changedObject = h;
+ });
+
+ AddStep("change start time", () => hitCircle.StartTime = 1000);
+ AddAssert("received change event", () => changedObject == hitCircle);
}
///
@@ -71,18 +79,22 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestAddedHitObjectStartTimeChangeEvent()
{
- var editorBeatmap = new EditorBeatmap(new OsuBeatmap());
-
+ EditorBeatmap editorBeatmap = null;
HitObject changedObject = null;
- editorBeatmap.StartTimeChanged += h => changedObject = h;
+
+ AddStep("add beatmap", () =>
+ {
+ Child = editorBeatmap = new EditorBeatmap(new OsuBeatmap());
+ editorBeatmap.HitObjectUpdated += h => changedObject = h;
+ });
var hitCircle = new HitCircle();
- editorBeatmap.Add(hitCircle);
- Assert.That(changedObject, Is.Null);
+ AddStep("add object", () => editorBeatmap.Add(hitCircle));
+ AddAssert("event not received", () => changedObject == null);
- hitCircle.StartTime = 1000;
- Assert.That(changedObject, Is.EqualTo(hitCircle));
+ AddStep("change start time", () => hitCircle.StartTime = 1000);
+ AddAssert("event received", () => changedObject == hitCircle);
}
///
@@ -95,7 +107,7 @@ namespace osu.Game.Tests.Beatmaps
var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } });
HitObject changedObject = null;
- editorBeatmap.StartTimeChanged += h => changedObject = h;
+ editorBeatmap.HitObjectUpdated += h => changedObject = h;
editorBeatmap.Remove(hitCircle);
Assert.That(changedObject, Is.Null);
diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs
new file mode 100644
index 0000000000..867af9c1b8
--- /dev/null
+++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs
@@ -0,0 +1,109 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Skinning;
+
+namespace osu.Game.Tests.NonVisual.Skinning
+{
+ [TestFixture]
+ public sealed class LegacySkinTextureFallbackTest
+ {
+ private static object[][] fallbackTestCases =
+ {
+ new object[]
+ {
+ // textures in store
+ new[] { "Gameplay/osu/followpoint@2x", "Gameplay/osu/followpoint" },
+ // requested component
+ "Gameplay/osu/followpoint",
+ // returned texture name & scale
+ "Gameplay/osu/followpoint@2x", 2
+ },
+ new object[]
+ {
+ new[] { "Gameplay/osu/followpoint@2x" },
+ "Gameplay/osu/followpoint",
+ "Gameplay/osu/followpoint@2x", 2
+ },
+ new object[]
+ {
+ new[] { "Gameplay/osu/followpoint" },
+ "Gameplay/osu/followpoint",
+ "Gameplay/osu/followpoint", 1
+ },
+ new object[]
+ {
+ new[] { "Gameplay/osu/followpoint", "followpoint@2x" },
+ "Gameplay/osu/followpoint",
+ "Gameplay/osu/followpoint", 1
+ },
+ new object[]
+ {
+ new[] { "followpoint@2x", "followpoint" },
+ "Gameplay/osu/followpoint",
+ "followpoint@2x", 2
+ },
+ new object[]
+ {
+ new[] { "followpoint@2x" },
+ "Gameplay/osu/followpoint",
+ "followpoint@2x", 2
+ },
+ new object[]
+ {
+ new[] { "followpoint" },
+ "Gameplay/osu/followpoint",
+ "followpoint", 1
+ },
+ };
+
+ [TestCaseSource(nameof(fallbackTestCases))]
+ public void TestFallbackOrder(string[] filesInStore, string requestedComponent, string expectedTexture, float expectedScale)
+ {
+ var textureStore = new TestTextureStore(filesInStore);
+ var legacySkin = new TestLegacySkin(textureStore);
+
+ var texture = legacySkin.GetTexture(requestedComponent);
+
+ Assert.IsNotNull(texture);
+ Assert.AreEqual(textureStore.Textures[expectedTexture], texture);
+ Assert.AreEqual(expectedScale, texture.ScaleAdjust);
+ }
+
+ [Test]
+ public void TestReturnNullOnFallbackFailure()
+ {
+ var textureStore = new TestTextureStore("sliderb", "hit100");
+ var legacySkin = new TestLegacySkin(textureStore);
+
+ var texture = legacySkin.GetTexture("Gameplay/osu/followpoint");
+
+ Assert.IsNull(texture);
+ }
+
+ private class TestLegacySkin : LegacySkin
+ {
+ public TestLegacySkin(TextureStore textureStore)
+ : base(new SkinInfo(), null, null, string.Empty)
+ {
+ Textures = textureStore;
+ }
+ }
+
+ private class TestTextureStore : TextureStore
+ {
+ public readonly Dictionary Textures;
+
+ public TestTextureStore(params string[] fileNames)
+ {
+ Textures = fileNames.ToDictionary(fileName => fileName, fileName => new Texture(1, 1));
+ }
+
+ public override Texture Get(string name) => Textures.GetValueOrDefault(name);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Resources/mania-skin-colours.ini b/osu.Game.Tests/Resources/mania-skin-colours.ini
new file mode 100644
index 0000000000..91d9696e0c
--- /dev/null
+++ b/osu.Game.Tests/Resources/mania-skin-colours.ini
@@ -0,0 +1,3 @@
+[Mania]
+Keys: 4
+ColourBarline: 50,50,50,50
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/mania-skin-duplicate.ini b/osu.Game.Tests/Resources/mania-skin-duplicate.ini
new file mode 100644
index 0000000000..2f4fa92c52
--- /dev/null
+++ b/osu.Game.Tests/Resources/mania-skin-duplicate.ini
@@ -0,0 +1,9 @@
+[Mania]
+Keys: 4
+ColumnWidth: 10,10,10,10
+HitPosition: 470
+
+[Mania]
+Keys: 4
+ColumnWidth: 20,20,20,20
+HitPosition: 460
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/mania-skin-extra-data.ini b/osu.Game.Tests/Resources/mania-skin-extra-data.ini
new file mode 100644
index 0000000000..e538b5335a
--- /dev/null
+++ b/osu.Game.Tests/Resources/mania-skin-extra-data.ini
@@ -0,0 +1,4 @@
+[Mania]
+Keys: 4
+ColumnWidth: 10,10,10,10,10,10,10
+HitPosition: 470
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/mania-skin-multiple.ini b/osu.Game.Tests/Resources/mania-skin-multiple.ini
new file mode 100644
index 0000000000..247c7738a0
--- /dev/null
+++ b/osu.Game.Tests/Resources/mania-skin-multiple.ini
@@ -0,0 +1,9 @@
+[Mania]
+Keys: 4
+ColumnWidth: 10,10,10,10
+HitPosition: 470
+
+[Mania]
+Keys: 2
+ColumnWidth: 20,20
+HitPosition: 460
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/mania-skin-single.ini b/osu.Game.Tests/Resources/mania-skin-single.ini
new file mode 100644
index 0000000000..3ae38fd75e
--- /dev/null
+++ b/osu.Game.Tests/Resources/mania-skin-single.ini
@@ -0,0 +1,4 @@
+[Mania]
+Keys: 4
+ColumnWidth: 10,10,10,10
+HitPosition: 470
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/mania-skin-zero-minwidth.ini b/osu.Game.Tests/Resources/mania-skin-zero-minwidth.ini
new file mode 100644
index 0000000000..fd22e2e299
--- /dev/null
+++ b/osu.Game.Tests/Resources/mania-skin-zero-minwidth.ini
@@ -0,0 +1,4 @@
+[Mania]
+Keys: 4
+ColumnWidth: 10,10,10,10
+WidthForNoteHeightScale: 0
\ No newline at end of file
diff --git a/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs
new file mode 100644
index 0000000000..e811979aed
--- /dev/null
+++ b/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs
@@ -0,0 +1,118 @@
+// 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.Game.IO;
+using osu.Game.Skinning;
+using osu.Game.Tests.Resources;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Skins
+{
+ [TestFixture]
+ public class LegacyManiaSkinDecoderTest
+ {
+ [Test]
+ public void TestParseSingleConfig()
+ {
+ var decoder = new LegacyManiaSkinDecoder();
+
+ using (var resStream = TestResources.OpenResource("mania-skin-single.ini"))
+ using (var stream = new LineBufferedReader(resStream))
+ {
+ var configs = decoder.Decode(stream);
+
+ Assert.That(configs.Count, Is.EqualTo(1));
+ Assert.That(configs[0].Keys, Is.EqualTo(4));
+ Assert.That(configs[0].ColumnWidth, Is.EquivalentTo(new float[] { 16, 16, 16, 16 }));
+ Assert.That(configs[0].HitPosition, Is.EqualTo(16));
+ }
+ }
+
+ [Test]
+ public void TestParseMultipleConfig()
+ {
+ var decoder = new LegacyManiaSkinDecoder();
+
+ using (var resStream = TestResources.OpenResource("mania-skin-multiple.ini"))
+ using (var stream = new LineBufferedReader(resStream))
+ {
+ var configs = decoder.Decode(stream);
+
+ Assert.That(configs.Count, Is.EqualTo(2));
+
+ Assert.That(configs[0].Keys, Is.EqualTo(4));
+ Assert.That(configs[0].ColumnWidth, Is.EquivalentTo(new float[] { 16, 16, 16, 16 }));
+ Assert.That(configs[0].HitPosition, Is.EqualTo(16));
+
+ Assert.That(configs[1].Keys, Is.EqualTo(2));
+ Assert.That(configs[1].ColumnWidth, Is.EquivalentTo(new float[] { 32, 32 }));
+ Assert.That(configs[1].HitPosition, Is.EqualTo(32));
+ }
+ }
+
+ [Test]
+ public void TestParseDuplicateConfig()
+ {
+ var decoder = new LegacyManiaSkinDecoder();
+
+ using (var resStream = TestResources.OpenResource("mania-skin-single.ini"))
+ using (var stream = new LineBufferedReader(resStream))
+ {
+ var configs = decoder.Decode(stream);
+
+ Assert.That(configs.Count, Is.EqualTo(1));
+ Assert.That(configs[0].Keys, Is.EqualTo(4));
+ Assert.That(configs[0].ColumnWidth, Is.EquivalentTo(new float[] { 16, 16, 16, 16 }));
+ Assert.That(configs[0].HitPosition, Is.EqualTo(16));
+ }
+ }
+
+ [Test]
+ public void TestParseWithUnnecessaryExtraData()
+ {
+ var decoder = new LegacyManiaSkinDecoder();
+
+ using (var resStream = TestResources.OpenResource("mania-skin-extra-data.ini"))
+ using (var stream = new LineBufferedReader(resStream))
+ {
+ var configs = decoder.Decode(stream);
+
+ Assert.That(configs.Count, Is.EqualTo(1));
+ Assert.That(configs[0].Keys, Is.EqualTo(4));
+ Assert.That(configs[0].ColumnWidth, Is.EquivalentTo(new float[] { 16, 16, 16, 16 }));
+ Assert.That(configs[0].HitPosition, Is.EqualTo(16));
+ }
+ }
+
+ [Test]
+ public void TestParseColours()
+ {
+ var decoder = new LegacyManiaSkinDecoder();
+
+ using (var resStream = TestResources.OpenResource("mania-skin-colours.ini"))
+ using (var stream = new LineBufferedReader(resStream))
+ {
+ var configs = decoder.Decode(stream);
+
+ Assert.That(configs.Count, Is.EqualTo(1));
+ Assert.That(configs[0].CustomColours, Contains.Key("ColourBarline").And.ContainValue(new Color4(50, 50, 50, 50)));
+ }
+ }
+
+ [Test]
+ public void TestMinimumColumnWidthFallsBackWhenZeroIsProvided()
+ {
+ var decoder = new LegacyManiaSkinDecoder();
+
+ using (var resStream = TestResources.OpenResource("mania-skin-zero-minwidth.ini"))
+ using (var stream = new LineBufferedReader(resStream))
+ {
+ var configs = decoder.Decode(stream);
+
+ Assert.That(configs.Count, Is.EqualTo(1));
+ Assert.That(configs[0].MinimumColumnWidth, Is.EqualTo(16));
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
index cef38bbbb8..aedf26ee75 100644
--- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
+++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
@@ -106,7 +106,7 @@ namespace osu.Game.Tests.Skins
var decoder = new LegacySkinDecoder();
using (var resStream = TestResources.OpenResource("skin-empty.ini"))
using (var stream = new LineBufferedReader(resStream))
- Assert.IsNull(decoder.Decode(stream).LegacyVersion);
+ Assert.That(decoder.Decode(stream).LegacyVersion, Is.EqualTo(1.0m));
}
}
}
diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs
index 35313ee858..685decf097 100644
--- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs
+++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.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.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
@@ -12,7 +13,10 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
using osu.Framework.Testing;
using osu.Game.Audio;
+using osu.Game.IO;
+using osu.Game.Rulesets.Osu;
using osu.Game.Skinning;
+using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual;
using osuTK.Graphics;
@@ -22,15 +26,15 @@ namespace osu.Game.Tests.Skins
[HeadlessTest]
public class TestSceneSkinConfigurationLookup : OsuTestScene
{
- private SkinSource source1;
- private SkinSource source2;
+ private UserSkinSource userSource;
+ private BeatmapSkinSource beatmapSource;
private SkinRequester requester;
[SetUp]
public void SetUp() => Schedule(() =>
{
- Add(new SkinProvidingContainer(source1 = new SkinSource())
- .WithChild(new SkinProvidingContainer(source2 = new SkinSource())
+ Add(new SkinProvidingContainer(userSource = new UserSkinSource())
+ .WithChild(new SkinProvidingContainer(beatmapSource = new BeatmapSkinSource())
.WithChild(requester = new SkinRequester())));
});
@@ -39,31 +43,31 @@ namespace osu.Game.Tests.Skins
{
AddStep("Add config values", () =>
{
- source1.Configuration.ConfigDictionary["Lookup"] = "source1";
- source2.Configuration.ConfigDictionary["Lookup"] = "source2";
+ userSource.Configuration.ConfigDictionary["Lookup"] = "user skin";
+ beatmapSource.Configuration.ConfigDictionary["Lookup"] = "beatmap skin";
});
- AddAssert("Check lookup finds source2", () => requester.GetConfig("Lookup")?.Value == "source2");
+ AddAssert("Check lookup finds beatmap skin", () => requester.GetConfig("Lookup")?.Value == "beatmap skin");
}
[Test]
public void TestFloatLookup()
{
- AddStep("Add config values", () => source1.Configuration.ConfigDictionary["FloatTest"] = "1.1");
+ AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["FloatTest"] = "1.1");
AddAssert("Check float parse lookup", () => requester.GetConfig("FloatTest")?.Value == 1.1f);
}
[Test]
public void TestBoolLookup()
{
- AddStep("Add config values", () => source1.Configuration.ConfigDictionary["BoolTest"] = "1");
+ AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["BoolTest"] = "1");
AddAssert("Check bool parse lookup", () => requester.GetConfig("BoolTest")?.Value == true);
}
[Test]
public void TestEnumLookup()
{
- AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Test"] = "Test2");
+ AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["Test"] = "Test2");
AddAssert("Check enum parse lookup", () => requester.GetConfig(LookupType.Test)?.Value == ValueType.Test2);
}
@@ -76,7 +80,7 @@ namespace osu.Game.Tests.Skins
[Test]
public void TestLookupNull()
{
- AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Lookup"] = null);
+ AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["Lookup"] = null);
AddAssert("Check lookup null", () =>
{
@@ -88,7 +92,7 @@ namespace osu.Game.Tests.Skins
[Test]
public void TestColourLookup()
{
- AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red);
+ AddStep("Add config colour", () => userSource.Configuration.CustomColours["Lookup"] = Color4.Red);
AddAssert("Check colour lookup", () => requester.GetConfig(new SkinCustomColourLookup("Lookup"))?.Value == Color4.Red);
}
@@ -101,7 +105,7 @@ namespace osu.Game.Tests.Skins
[Test]
public void TestWrongColourType()
{
- AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red);
+ AddStep("Add config colour", () => userSource.Configuration.CustomColours["Lookup"] = Color4.Red);
AddAssert("perform incorrect lookup", () =>
{
@@ -127,26 +131,51 @@ namespace osu.Game.Tests.Skins
[Test]
public void TestEmptyComboColoursNoFallback()
{
- AddStep("Add custom combo colours to source1", () => source1.Configuration.AddComboColours(
+ AddStep("Add custom combo colours to user skin", () => userSource.Configuration.AddComboColours(
new Color4(100, 150, 200, 255),
new Color4(55, 110, 166, 255),
new Color4(75, 125, 175, 255)
));
- AddStep("Disallow default colours fallback in source2", () => source2.Configuration.AllowDefaultComboColoursFallback = false);
+ AddStep("Disallow default colours fallback in beatmap skin", () => beatmapSource.Configuration.AllowDefaultComboColoursFallback = false);
- AddAssert("Check retrieved combo colours from source1", () =>
- requester.GetConfig>(GlobalSkinColours.ComboColours)?.Value?.SequenceEqual(source1.Configuration.ComboColours) ?? false);
+ AddAssert("Check retrieved combo colours from user skin", () =>
+ requester.GetConfig>(GlobalSkinColours.ComboColours)?.Value?.SequenceEqual(userSource.Configuration.ComboColours) ?? false);
}
[Test]
- public void TestLegacyVersionLookup()
+ public void TestNullBeatmapVersionFallsBackToUserSkin()
{
- AddStep("Set source1 version 2.3", () => source1.Configuration.LegacyVersion = 2.3m);
- AddStep("Set source2 version null", () => source2.Configuration.LegacyVersion = null);
+ AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = 2.3m);
+ AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null);
AddAssert("Check legacy version lookup", () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == 2.3m);
}
+ [Test]
+ public void TestSetBeatmapVersionNoFallback()
+ {
+ AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = 2.3m);
+ AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = 1.7m);
+ AddAssert("Check legacy version lookup", () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == 1.7m);
+ }
+
+ [Test]
+ public void TestNullBeatmapAndUserVersionFallsBackToLatest()
+ {
+ AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = null);
+ AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null);
+ AddAssert("Check legacy version lookup",
+ () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == LegacySkinConfiguration.LATEST_VERSION);
+ }
+
+ [Test]
+ public void TestIniWithNoVersionFallsBackTo1()
+ {
+ AddStep("Parse skin with no version", () => userSource.Configuration = new LegacySkinDecoder().Decode(new LineBufferedReader(new MemoryStream())));
+ AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null);
+ AddAssert("Check legacy version lookup", () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == 1.0m);
+ }
+
public enum LookupType
{
Test
@@ -159,14 +188,22 @@ namespace osu.Game.Tests.Skins
Test3
}
- public class SkinSource : LegacySkin
+ public class UserSkinSource : LegacySkin
{
- public SkinSource()
+ public UserSkinSource()
: base(new SkinInfo(), null, null, string.Empty)
{
}
}
+ public class BeatmapSkinSource : LegacyBeatmapSkin
+ {
+ public BeatmapSkinSource()
+ : base(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, null, null)
+ {
+ }
+ }
+
public class SkinRequester : Drawable, ISkin
{
private ISkinSource skin;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs
index ff25e609c1..91d6f2f143 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs
@@ -26,16 +26,8 @@ namespace osu.Game.Tests.Visual.Gameplay
private readonly IReadOnlyList testBreaks = new List
{
- new BreakPeriod
- {
- StartTime = 1000,
- EndTime = 5000,
- },
- new BreakPeriod
- {
- StartTime = 6000,
- EndTime = 13500,
- },
+ new BreakPeriod(1000, 5000),
+ new BreakPeriod(6000, 13500),
};
public TestSceneBreakTracker()
@@ -70,7 +62,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestNoEffectsBreak()
{
- var shortBreak = new BreakPeriod { EndTime = 500 };
+ var shortBreak = new BreakPeriod(0, 500);
setClock(true);
loadBreaksStep("short break", new[] { shortBreak });
@@ -127,13 +119,12 @@ namespace osu.Game.Tests.Visual.Gameplay
private void addShowBreakStep(double seconds)
{
- AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = breakTracker.Breaks = new List
+ AddStep($"show '{seconds}s' break", () =>
{
- new BreakPeriod
+ breakOverlay.Breaks = breakTracker.Breaks = new List
{
- StartTime = Clock.CurrentTime,
- EndTime = Clock.CurrentTime + seconds * 1000,
- }
+ new BreakPeriod(Clock.CurrentTime, Clock.CurrentTime + seconds * 1000)
+ };
});
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
index ec94053679..3b91243fee 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
@@ -43,16 +43,15 @@ namespace osu.Game.Tests.Visual.Gameplay
{
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true),
- new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit),
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
}
},
};
});
- AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 30, 50 }));
+ AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 50 }));
AddStep("adjust scale", () => fill.Scale = new Vector2(2));
- AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 30, 50 }));
+ AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 50 }));
}
[Test]
@@ -74,7 +73,6 @@ namespace osu.Game.Tests.Visual.Gameplay
Children = new[]
{
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
- new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true),
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit),
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
}
@@ -82,9 +80,9 @@ namespace osu.Game.Tests.Visual.Gameplay
};
});
- AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 30, 50, 30 }));
+ AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 50, 30 }));
AddStep("adjust scale", () => fill.Scale = new Vector2(2));
- AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 30, 50, 30 }));
+ AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 50, 30 }));
}
[Test]
@@ -182,7 +180,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public new Drawable Drawable => base.Drawable;
public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null,
- ConfineMode confineMode = ConfineMode.ScaleDownToFit)
+ ConfineMode confineMode = ConfineMode.ScaleToFit)
: base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
{
}
diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
index 1ad4d9dca9..33811f9529 100644
--- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
+++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
@@ -22,7 +22,6 @@ namespace osu.Game.Tests.Visual.Menus
{
typeof(StartupScreen),
typeof(IntroScreen),
- typeof(OsuScreen),
typeof(IntroTestScene),
};
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index 03a19b6690..2294cd6966 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
- Position = new Vector2(0, 25),
+ Position = new Vector2(-5, 25),
Current = { BindTarget = modSelect.SelectedMods }
}
};
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs
index 1cd68d1fdd..c81ec9f663 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs
@@ -8,7 +8,6 @@ using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
-using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes;
using osuTK.Graphics;
@@ -100,21 +99,21 @@ namespace osu.Game.Tests.Visual.UserInterface
private class TestNoBackgroundHeader : OverlayHeader
{
- protected override ScreenTitle CreateTitle() => new TestTitle();
+ protected override OverlayTitle CreateTitle() => new TestTitle();
}
private class TestNoControlHeader : OverlayHeader
{
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/changelog");
- protected override ScreenTitle CreateTitle() => new TestTitle();
+ protected override OverlayTitle CreateTitle() => new TestTitle();
}
private class TestStringTabControlHeader : TabControlOverlayHeader
{
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
- protected override ScreenTitle CreateTitle() => new TestTitle();
+ protected override OverlayTitle CreateTitle() => new TestTitle();
protected override Drawable CreateTitleContent() => new OverlayRulesetSelector();
@@ -129,7 +128,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/rankings");
- protected override ScreenTitle CreateTitle() => new TestTitle();
+ protected override OverlayTitle CreateTitle() => new TestTitle();
}
private enum TestEnum
@@ -141,7 +140,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private class TestBreadcrumbControlHeader : BreadcrumbControlOverlayHeader
{
- protected override ScreenTitle CreateTitle() => new TestTitle();
+ protected override OverlayTitle CreateTitle() => new TestTitle();
public TestBreadcrumbControlHeader()
{
@@ -151,15 +150,13 @@ namespace osu.Game.Tests.Visual.UserInterface
}
}
- private class TestTitle : ScreenTitle
+ private class TestTitle : OverlayTitle
{
public TestTitle()
{
Title = "title";
- Section = "section";
+ IconTexture = "Icons/changelog";
}
-
- protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
}
}
}
diff --git a/osu.Game.Tournament/Components/TourneyVideo.cs b/osu.Game.Tournament/Components/TourneyVideo.cs
index bc66fad8c1..317c5f6a56 100644
--- a/osu.Game.Tournament/Components/TourneyVideo.cs
+++ b/osu.Game.Tournament/Components/TourneyVideo.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Tournament.Components
{
private readonly string filename;
private readonly bool drawFallbackGradient;
- private VideoSprite video;
+ private Video video;
private ManualClock manualClock;
@@ -33,7 +33,7 @@ namespace osu.Game.Tournament.Components
if (stream != null)
{
- InternalChild = video = new VideoSprite(stream, false)
+ InternalChild = video = new Video(stream, false)
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
diff --git a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs
index bdaa1ae7fd..fa03518c47 100644
--- a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs
+++ b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs
@@ -22,6 +22,8 @@ namespace osu.Game.Tournament.Screens.Ladder
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
+ public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false;
+
protected override void OnDrag(DragEvent e)
{
this.MoveTo(target += e.Delta, 1000, Easing.OutQuint);
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index abb3f8ac42..6542866936 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -87,7 +87,7 @@ namespace osu.Game.Beatmaps
protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz";
- protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
+ protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
{
if (archive != null)
beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files);
@@ -103,7 +103,19 @@ namespace osu.Game.Beatmaps
validateOnlineIds(beatmapSet);
- return updateQueue.UpdateAsync(beatmapSet, cancellationToken);
+ bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0);
+
+ await updateQueue.UpdateAsync(beatmapSet, cancellationToken);
+
+ // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID.
+ if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0))
+ {
+ if (beatmapSet.OnlineBeatmapSetID != null)
+ {
+ beatmapSet.OnlineBeatmapSetID = null;
+ LogForModel(beatmapSet, "Disassociating beatmap set ID due to loss of all beatmap IDs");
+ }
+ }
}
protected override void PreImport(BeatmapSetInfo beatmapSet)
@@ -447,12 +459,15 @@ namespace osu.Game.Beatmaps
var res = req.Result;
- beatmap.Status = res.Status;
- beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
- beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
- beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
+ if (res != null)
+ {
+ beatmap.Status = res.Status;
+ beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
+ beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
+ beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
- LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
+ LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
+ }
}
catch (Exception e)
{
diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs
index 45122f6312..845ac20db0 100644
--- a/osu.Game/Beatmaps/Formats/Decoder.cs
+++ b/osu.Game/Beatmaps/Formats/Decoder.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Beatmaps.Formats
if (line == null)
throw new IOException("Unknown file format (null)");
- var decoder = typedDecoders.Select(d => line.StartsWith(d.Key, StringComparison.InvariantCulture) ? d.Value : null).FirstOrDefault();
+ var decoder = typedDecoders.Where(d => line.StartsWith(d.Key, StringComparison.InvariantCulture)).Select(d => d.Value).FirstOrDefault();
// it's important the magic does NOT get consumed here, since sometimes it's part of the structure
// (see JsonBeatmapDecoder - the magic string is the opening brace)
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index f5b27eddd2..33bb9774df 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -305,12 +305,9 @@ namespace osu.Game.Beatmaps.Formats
case LegacyEventType.Break:
double start = getOffsetTime(Parsing.ParseDouble(split[1]));
+ double end = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[2])));
- var breakEvent = new BreakPeriod
- {
- StartTime = start,
- EndTime = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[2])))
- };
+ var breakEvent = new BreakPeriod(start, end);
if (!breakEvent.HasEffect)
return;
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index ec2ca30535..12f2c58e35 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -111,7 +111,7 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"Source: {beatmap.Metadata.Source}"));
writer.WriteLine(FormattableString.Invariant($"Tags: {beatmap.Metadata.Tags}"));
writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineBeatmapID ?? 0}"));
- writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID ?? -1}"));
+ writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID ?? -1}"));
}
private void handleDifficulty(TextWriter writer)
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index e28e235788..561707f9ef 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -41,6 +41,7 @@ namespace osu.Game.Beatmaps.Formats
section = Section.None;
}
+ OnBeginNewSection(section);
continue;
}
@@ -57,6 +58,14 @@ namespace osu.Game.Beatmaps.Formats
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.AsSpan().TrimStart().StartsWith("//".AsSpan(), StringComparison.Ordinal);
+ ///
+ /// Invoked when a new has been entered.
+ ///
+ /// The entered .
+ protected virtual void OnBeginNewSection(Section section)
+ {
+ }
+
protected virtual void ParseLine(T output, Section section, string line)
{
line = StripComments(line);
@@ -64,7 +73,7 @@ namespace osu.Game.Beatmaps.Formats
switch (section)
{
case Section.Colours:
- handleColours(output, line);
+ HandleColours(output, line);
return;
}
}
@@ -78,7 +87,7 @@ namespace osu.Game.Beatmaps.Formats
return line;
}
- private void handleColours(T output, string line)
+ protected void HandleColours(TModel output, string line)
{
var pair = SplitKeyVal(line);
@@ -139,7 +148,8 @@ namespace osu.Game.Beatmaps.Formats
Colours,
HitObjects,
Variables,
- Fonts
+ Fonts,
+ Mania
}
internal class LegacyDifficultyControlPoint : DifficultyControlPoint
diff --git a/osu.Game/Beatmaps/Timing/BreakPeriod.cs b/osu.Game/Beatmaps/Timing/BreakPeriod.cs
index 5d79c7a86b..bb8ae4a66a 100644
--- a/osu.Game/Beatmaps/Timing/BreakPeriod.cs
+++ b/osu.Game/Beatmaps/Timing/BreakPeriod.cs
@@ -32,6 +32,17 @@ namespace osu.Game.Beatmaps.Timing
///
public bool HasEffect => Duration >= MIN_BREAK_DURATION;
+ ///
+ /// Constructs a new break period.
+ ///
+ /// The start time of the break period.
+ /// The end time of the break period.
+ public BreakPeriod(double startTime, double endTime)
+ {
+ StartTime = startTime;
+ EndTime = endTime;
+ }
+
///
/// Whether this break contains a specified time.
///
diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs
new file mode 100644
index 0000000000..1790eb608e
--- /dev/null
+++ b/osu.Game/Extensions/DrawableExtensions.cs
@@ -0,0 +1,32 @@
+// 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.Input.Bindings;
+using osu.Framework.Threading;
+
+namespace osu.Game.Extensions
+{
+ public static class DrawableExtensions
+ {
+ ///
+ /// Helper method that is used while doesn't support repetitions of .
+ /// Simulates repetitions by continually invoking a delegate according to the default key repeat rate.
+ ///
+ ///
+ /// The returned delegate can be cancelled to stop repeat events from firing (usually in ).
+ ///
+ /// The which is handling the repeat.
+ /// The to schedule repetitions on.
+ /// The to be invoked once immediately and with every repetition.
+ /// A which can be cancelled to stop the repeat events from firing.
+ public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action)
+ {
+ action();
+
+ ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + 250, 70);
+ scheduler.Add(repeatDelegate);
+ return repeatDelegate;
+ }
+ }
+}
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
index ca9f1330f9..c2feca171b 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
@@ -113,13 +113,13 @@ namespace osu.Game.Graphics.UserInterface
private const float transition_length = 500;
- private void fadeActive()
+ protected void FadeHovered()
{
Bar.FadeIn(transition_length, Easing.OutQuint);
Text.FadeColour(Color4.White, transition_length, Easing.OutQuint);
}
- private void fadeInactive()
+ protected void FadeUnhovered()
{
Bar.FadeOut(transition_length, Easing.OutQuint);
Text.FadeColour(AccentColour, transition_length, Easing.OutQuint);
@@ -128,14 +128,14 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnHover(HoverEvent e)
{
if (!Active.Value)
- fadeActive();
+ FadeHovered();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
if (!Active.Value)
- fadeInactive();
+ FadeUnhovered();
}
[BackgroundDependencyLoader]
@@ -172,13 +172,19 @@ namespace osu.Game.Graphics.UserInterface
},
new HoverClickSounds()
};
-
- Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Torus, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
}
- protected override void OnActivated() => fadeActive();
+ protected override void OnActivated()
+ {
+ Text.Font = Text.Font.With(weight: FontWeight.Bold);
+ FadeHovered();
+ }
- protected override void OnDeactivated() => fadeInactive();
+ protected override void OnDeactivated()
+ {
+ Text.Font = Text.Font.With(weight: FontWeight.Medium);
+ FadeUnhovered();
+ }
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs
deleted file mode 100644
index ecd0508258..0000000000
--- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs
+++ /dev/null
@@ -1,102 +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 osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Game.Graphics.Sprites;
-using osuTK;
-using osuTK.Graphics;
-
-namespace osu.Game.Graphics.UserInterface
-{
- public abstract class ScreenTitle : CompositeDrawable, IHasAccentColour
- {
- public const float ICON_WIDTH = ICON_SIZE + spacing;
-
- public const float ICON_SIZE = 25;
- private const float spacing = 6;
- private const int text_offset = 2;
-
- private SpriteIcon iconSprite;
- private readonly OsuSpriteText titleText, pageText;
-
- protected IconUsage Icon
- {
- set
- {
- if (iconSprite == null)
- throw new InvalidOperationException($"Cannot use {nameof(Icon)} with a custom {nameof(CreateIcon)} function.");
-
- iconSprite.Icon = value;
- }
- }
-
- protected string Title
- {
- set => titleText.Text = value;
- }
-
- protected string Section
- {
- set => pageText.Text = value;
- }
-
- public Color4 AccentColour
- {
- get => pageText.Colour;
- set => pageText.Colour = value;
- }
-
- protected virtual Drawable CreateIcon() => iconSprite = new SpriteIcon
- {
- Size = new Vector2(ICON_SIZE),
- };
-
- protected ScreenTitle()
- {
- AutoSizeAxes = Axes.Both;
-
- InternalChildren = new Drawable[]
- {
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Spacing = new Vector2(spacing, 0),
- Direction = FillDirection.Horizontal,
- Children = new[]
- {
- CreateIcon().With(t =>
- {
- t.Anchor = Anchor.Centre;
- t.Origin = Anchor.Centre;
- }),
- titleText = new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold),
- Margin = new MarginPadding { Bottom = text_offset }
- },
- new Circle
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Size = new Vector2(4),
- Colour = Color4.Gray,
- },
- pageText = new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Font = OsuFont.GetFont(size: 20),
- Margin = new MarginPadding { Bottom = text_offset }
- }
- }
- },
- };
- }
- }
-}
diff --git a/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs b/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs
deleted file mode 100644
index c2a13970de..0000000000
--- a/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs
+++ /dev/null
@@ -1,40 +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.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
-using osuTK;
-
-namespace osu.Game.Graphics.UserInterface
-{
- ///
- /// A custom icon class for use with based off a texture resource.
- ///
- public class ScreenTitleTextureIcon : CompositeDrawable
- {
- private readonly string textureName;
-
- public ScreenTitleTextureIcon(string textureName)
- {
- this.textureName = textureName;
- }
-
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- Size = new Vector2(ScreenTitle.ICON_SIZE);
-
- InternalChild = new Sprite
- {
- RelativeSizeAxes = Axes.Both,
- Texture = textures.Get(textureName),
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- FillMode = FillMode.Fit
- };
- }
- }
-}
diff --git a/osu.Game/IO/LineBufferedReader.cs b/osu.Game/IO/LineBufferedReader.cs
index aab761afd8..018321dc9a 100644
--- a/osu.Game/IO/LineBufferedReader.cs
+++ b/osu.Game/IO/LineBufferedReader.cs
@@ -17,9 +17,9 @@ namespace osu.Game.IO
private readonly StreamReader streamReader;
private readonly Queue lineBuffer;
- public LineBufferedReader(Stream stream)
+ public LineBufferedReader(Stream stream, bool leaveOpen = false)
{
- streamReader = new StreamReader(stream);
+ streamReader = new StreamReader(stream, Encoding.UTF8, true, 1024, leaveOpen);
lineBuffer = new Queue();
}
diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs
index 30c1018c1e..6a6c7b72a8 100644
--- a/osu.Game/Online/API/APIRequest.cs
+++ b/osu.Game/Online/API/APIRequest.cs
@@ -12,11 +12,11 @@ namespace osu.Game.Online.API
/// An API request with a well-defined response type.
///
/// Type of the response (used for deserialisation).
- public abstract class APIRequest : APIRequest
+ public abstract class APIRequest : APIRequest where T : class
{
protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest(Uri);
- public T Result => ((OsuJsonWebRequest)WebRequest).ResponseObject;
+ public T Result => ((OsuJsonWebRequest)WebRequest)?.ResponseObject;
protected APIRequest()
{
diff --git a/osu.Game/Online/API/Requests/GetRankingsRequest.cs b/osu.Game/Online/API/Requests/GetRankingsRequest.cs
index 941691c4c1..ddc3298ca7 100644
--- a/osu.Game/Online/API/Requests/GetRankingsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetRankingsRequest.cs
@@ -6,7 +6,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests
{
- public abstract class GetRankingsRequest : APIRequest
+ public abstract class GetRankingsRequest : APIRequest where TModel : class
{
private readonly RulesetInfo ruleset;
private readonly int page;
diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs
index 52e12f04ee..bddc34a0dc 100644
--- a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs
+++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs
@@ -6,7 +6,7 @@ using osu.Framework.IO.Network;
namespace osu.Game.Online.API.Requests
{
- public abstract class PaginatedAPIRequest : APIRequest
+ public abstract class PaginatedAPIRequest : APIRequest where T : class
{
private readonly int page;
private readonly int itemsPerPage;
diff --git a/osu.Game/Online/Leaderboards/UpdateableRank.cs b/osu.Game/Online/Leaderboards/UpdateableRank.cs
index d9e8957281..8f74fd84fe 100644
--- a/osu.Game/Online/Leaderboards/UpdateableRank.cs
+++ b/osu.Game/Online/Leaderboards/UpdateableRank.cs
@@ -7,23 +7,31 @@ using osu.Game.Scoring;
namespace osu.Game.Online.Leaderboards
{
- public class UpdateableRank : ModelBackedDrawable
+ public class UpdateableRank : ModelBackedDrawable
{
- public ScoreRank Rank
+ public ScoreRank? Rank
{
get => Model;
set => Model = value;
}
- public UpdateableRank(ScoreRank rank)
+ public UpdateableRank(ScoreRank? rank)
{
Rank = rank;
}
- protected override Drawable CreateDrawable(ScoreRank rank) => new DrawableRank(rank)
+ protected override Drawable CreateDrawable(ScoreRank? rank)
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- };
+ if (rank.HasValue)
+ {
+ return new DrawableRank(rank.Value)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+ }
+
+ return null;
+ }
}
}
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs
index 5af92914de..1bab200fec 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs
@@ -1,24 +1,19 @@
// 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.Graphics.UserInterface;
-
namespace osu.Game.Overlays.BeatmapListing
{
public class BeatmapListingHeader : OverlayHeader
{
- protected override ScreenTitle CreateTitle() => new BeatmapListingTitle();
+ protected override OverlayTitle CreateTitle() => new BeatmapListingTitle();
- private class BeatmapListingTitle : ScreenTitle
+ private class BeatmapListingTitle : OverlayTitle
{
public BeatmapListingTitle()
{
- Title = @"beatmap";
- Section = @"listing";
+ Title = "beatmap listing";
+ IconTexture = "Icons/changelog";
}
-
- protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
}
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs
index e5e3e276d5..4626589d81 100644
--- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs
@@ -3,7 +3,6 @@
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.BeatmapSet
@@ -14,22 +13,20 @@ namespace osu.Game.Overlays.BeatmapSet
public BeatmapRulesetSelector RulesetSelector { get; private set; }
- protected override ScreenTitle CreateTitle() => new BeatmapHeaderTitle();
+ protected override OverlayTitle CreateTitle() => new BeatmapHeaderTitle();
protected override Drawable CreateTitleContent() => RulesetSelector = new BeatmapRulesetSelector
{
Current = Ruleset
};
- private class BeatmapHeaderTitle : ScreenTitle
+ private class BeatmapHeaderTitle : OverlayTitle
{
public BeatmapHeaderTitle()
{
- Title = @"beatmap";
- Section = @"info";
+ Title = "beatmap info";
+ IconTexture = "Icons/changelog";
}
-
- protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
}
}
}
diff --git a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs
index 1d8411dfcc..81315f9638 100644
--- a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs
+++ b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
@@ -16,6 +17,13 @@ namespace osu.Game.Overlays
public OverlayHeaderBreadcrumbControl()
{
RelativeSizeAxes = Axes.X;
+ Height = 47;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OverlayColourProvider colourProvider)
+ {
+ AccentColour = colourProvider.Light2;
}
protected override TabItem CreateTabItem(string value) => new ControlTabItem(value);
@@ -27,10 +35,18 @@ namespace osu.Game.Overlays
public ControlTabItem(string value)
: base(value)
{
+ RelativeSizeAxes = Axes.Y;
Text.Font = Text.Font.With(size: 14);
- Chevron.Y = 3;
+ Text.Anchor = Anchor.CentreLeft;
+ Text.Origin = Anchor.CentreLeft;
+ Chevron.Y = 1;
Bar.Height = 0;
}
+
+ // base OsuTabItem makes font bold on activation, we don't want that here
+ protected override void OnActivated() => FadeHovered();
+
+ protected override void OnDeactivated() => FadeUnhovered();
}
}
}
diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
index 532efeb4bd..050bdea03a 100644
--- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs
+++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
@@ -9,7 +9,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Overlays.Changelog
@@ -50,8 +49,6 @@ namespace osu.Game.Overlays.Changelog
streamsBackground.Colour = colourProvider.Background5;
}
- private ChangelogHeaderTitle title;
-
private void showBuild(ValueChangedEvent e)
{
if (e.OldValue != null)
@@ -63,14 +60,11 @@ namespace osu.Game.Overlays.Changelog
Current.Value = e.NewValue.ToString();
updateCurrentStream();
-
- title.Version = e.NewValue.UpdateStream.DisplayName;
}
else
{
Current.Value = listing_string;
Streams.Current.Value = null;
- title.Version = null;
}
}
@@ -100,7 +94,7 @@ namespace osu.Game.Overlays.Changelog
}
};
- protected override ScreenTitle CreateTitle() => title = new ChangelogHeaderTitle();
+ protected override OverlayTitle CreateTitle() => new ChangelogHeaderTitle();
public void Populate(List streams)
{
@@ -116,20 +110,13 @@ namespace osu.Game.Overlays.Changelog
Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == Build.Value.UpdateStream.Name);
}
- private class ChangelogHeaderTitle : ScreenTitle
+ private class ChangelogHeaderTitle : OverlayTitle
{
- public string Version
- {
- set => Section = value ?? listing_string;
- }
-
public ChangelogHeaderTitle()
{
Title = "changelog";
- Version = null;
+ IconTexture = "Icons/changelog";
}
-
- protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
}
}
}
diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs
index b55e3ffba0..8214c71b3a 100644
--- a/osu.Game/Overlays/News/NewsHeader.cs
+++ b/osu.Game/Overlays/News/NewsHeader.cs
@@ -3,7 +3,6 @@
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Game.Graphics.UserInterface;
using System;
namespace osu.Game.Overlays.News
@@ -12,8 +11,6 @@ namespace osu.Game.Overlays.News
{
private const string front_page_string = "frontpage";
- private NewsHeaderTitle title;
-
public readonly Bindable Post = new Bindable(null);
public Action ShowFrontPage;
@@ -40,36 +37,24 @@ namespace osu.Game.Overlays.News
{
TabControl.AddItem(e.NewValue);
Current.Value = e.NewValue;
-
- title.IsReadingPost = true;
}
else
{
Current.Value = front_page_string;
- title.IsReadingPost = false;
}
}
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
- protected override ScreenTitle CreateTitle() => title = new NewsHeaderTitle();
+ protected override OverlayTitle CreateTitle() => new NewsHeaderTitle();
- private class NewsHeaderTitle : ScreenTitle
+ private class NewsHeaderTitle : OverlayTitle
{
- private const string post_string = "post";
-
- public bool IsReadingPost
- {
- set => Section = value ? post_string : front_page_string;
- }
-
public NewsHeaderTitle()
{
Title = "news";
- IsReadingPost = false;
+ IconTexture = "Icons/news";
}
-
- protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/news");
}
}
}
diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs
index bedf8e5435..4ac0f697c3 100644
--- a/osu.Game/Overlays/OverlayHeader.cs
+++ b/osu.Game/Overlays/OverlayHeader.cs
@@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
namespace osu.Game.Overlays
@@ -14,7 +13,6 @@ namespace osu.Game.Overlays
public abstract class OverlayHeader : Container
{
private readonly Box titleBackground;
- private readonly ScreenTitle title;
protected readonly FillFlowContainer HeaderInfo;
@@ -57,11 +55,10 @@ namespace osu.Game.Overlays
Padding = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
- Vertical = 10,
},
Children = new[]
{
- title = CreateTitle().With(title =>
+ CreateTitle().With(title =>
{
title.Anchor = Anchor.CentreLeft;
title.Origin = Anchor.CentreLeft;
@@ -86,7 +83,6 @@ namespace osu.Game.Overlays
private void load(OverlayColourProvider colourProvider)
{
titleBackground.Colour = colourProvider.Dark5;
- title.AccentColour = colourProvider.Highlight1;
}
[NotNull]
@@ -96,11 +92,11 @@ namespace osu.Game.Overlays
protected virtual Drawable CreateBackground() => Empty();
///
- /// Creates a on the opposite side of the . Used mostly to create .
+ /// Creates a on the opposite side of the . Used mostly to create .
///
[NotNull]
protected virtual Drawable CreateTitleContent() => Empty();
- protected abstract ScreenTitle CreateTitle();
+ protected abstract OverlayTitle CreateTitle();
}
}
diff --git a/osu.Game/Overlays/OverlayRulesetSelector.cs b/osu.Game/Overlays/OverlayRulesetSelector.cs
index b73d38eeb3..8c44157f78 100644
--- a/osu.Game/Overlays/OverlayRulesetSelector.cs
+++ b/osu.Game/Overlays/OverlayRulesetSelector.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Overlays
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
- Spacing = new Vector2(25, 0),
+ Spacing = new Vector2(20, 0),
};
}
}
diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs
index 9b4dd5ba1e..9d4afc94d1 100644
--- a/osu.Game/Overlays/OverlayRulesetTabItem.cs
+++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs
@@ -12,6 +12,7 @@ using osu.Game.Rulesets;
using osuTK.Graphics;
using osuTK;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Overlays
{
@@ -53,6 +54,8 @@ namespace osu.Game.Overlays
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Text = value.Name,
+ Font = OsuFont.GetFont(size: 14),
+ ShadowColour = Color4.Black.Opacity(0.75f)
}
},
new HoverClickSounds()
diff --git a/osu.Game/Overlays/OverlayTabControl.cs b/osu.Game/Overlays/OverlayTabControl.cs
index aa96f0e19b..a1cbf2c1e7 100644
--- a/osu.Game/Overlays/OverlayTabControl.cs
+++ b/osu.Game/Overlays/OverlayTabControl.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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
@@ -35,17 +36,22 @@ namespace osu.Game.Overlays
protected OverlayTabControl()
{
TabContainer.Masking = false;
- TabContainer.Spacing = new Vector2(15, 0);
+ TabContainer.Spacing = new Vector2(20, 0);
AddInternal(bar = new Box
{
RelativeSizeAxes = Axes.X,
- Height = 2,
Anchor = Anchor.BottomLeft,
- Origin = Anchor.CentreLeft
+ Origin = Anchor.BottomLeft
});
}
+ [BackgroundDependencyLoader]
+ private void load(OverlayColourProvider colourProvider)
+ {
+ AccentColour = colourProvider.Highlight1;
+ }
+
protected override Dropdown CreateDropdown() => null;
protected override TabItem CreateTabItem(T value) => new OverlayTabItem(value);
@@ -90,7 +96,7 @@ namespace osu.Game.Overlays
Bar = new ExpandingBar
{
Anchor = Anchor.BottomCentre,
- ExpandedSize = 7.5f,
+ ExpandedSize = 5f,
CollapsedSize = 0
},
new HoverClickSounds()
@@ -119,6 +125,7 @@ namespace osu.Game.Overlays
{
HoverAction();
Text.Font = Text.Font.With(weight: FontWeight.Bold);
+ Text.FadeColour(Color4.White, 120, Easing.InQuad);
}
protected override void OnDeactivated()
@@ -135,11 +142,7 @@ namespace osu.Game.Overlays
OnDeactivated();
}
- protected virtual void HoverAction()
- {
- Bar.Expand();
- Text.FadeColour(Color4.White, 120, Easing.InQuad);
- }
+ protected virtual void HoverAction() => Bar.Expand();
protected virtual void UnhoverAction()
{
diff --git a/osu.Game/Overlays/OverlayTitle.cs b/osu.Game/Overlays/OverlayTitle.cs
new file mode 100644
index 0000000000..1c9567428c
--- /dev/null
+++ b/osu.Game/Overlays/OverlayTitle.cs
@@ -0,0 +1,80 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osuTK;
+
+namespace osu.Game.Overlays
+{
+ public abstract class OverlayTitle : CompositeDrawable
+ {
+ private readonly OsuSpriteText title;
+ private readonly Container icon;
+
+ protected string Title
+ {
+ set => title.Text = value;
+ }
+
+ protected string IconTexture
+ {
+ set => icon.Child = new OverlayTitleIcon(value);
+ }
+
+ protected OverlayTitle()
+ {
+ AutoSizeAxes = Axes.Both;
+
+ InternalChild = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Spacing = new Vector2(10, 0),
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ icon = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Margin = new MarginPadding { Horizontal = 5 }, // compensates for osu-web sprites having around 5px of whitespace on each side
+ Size = new Vector2(30)
+ },
+ title = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular),
+ Margin = new MarginPadding { Vertical = 17.5f } // 15px padding + 2.5px line-height difference compensation
+ }
+ }
+ };
+ }
+
+ private class OverlayTitleIcon : Sprite
+ {
+ private readonly string textureName;
+
+ public OverlayTitleIcon(string textureName)
+ {
+ this.textureName = textureName;
+
+ RelativeSizeAxes = Axes.Both;
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ FillMode = FillMode.Fit;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Texture = textures.Get(textureName);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index f7c09e33c1..0161d91daa 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
@@ -87,19 +86,17 @@ namespace osu.Game.Overlays.Profile
}
};
- protected override ScreenTitle CreateTitle() => new ProfileHeaderTitle();
+ protected override OverlayTitle CreateTitle() => new ProfileHeaderTitle();
private void updateDisplay(User user) => coverContainer.User = user;
- private class ProfileHeaderTitle : ScreenTitle
+ private class ProfileHeaderTitle : OverlayTitle
{
public ProfileHeaderTitle()
{
- Title = "player";
- Section = "info";
+ Title = "player info";
+ IconTexture = "Icons/profile";
}
-
- protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/profile");
}
}
}
diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs
index 99325aa1da..e30c6f07a8 100644
--- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs
+++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs
@@ -3,7 +3,6 @@
using osu.Framework.Graphics;
using osu.Framework.Bindables;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Users;
@@ -18,33 +17,21 @@ namespace osu.Game.Overlays.Rankings
private OverlayRulesetSelector rulesetSelector;
private CountryFilter countryFilter;
- protected override ScreenTitle CreateTitle() => new RankingsTitle
- {
- Scope = { BindTarget = Current }
- };
+ protected override OverlayTitle CreateTitle() => new RankingsTitle();
protected override Drawable CreateTitleContent() => rulesetSelector = new OverlayRulesetSelector();
protected override Drawable CreateContent() => countryFilter = new CountryFilter();
- protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/rankings");
+ protected override Drawable CreateBackground() => new OverlayHeaderBackground("Headers/rankings");
- private class RankingsTitle : ScreenTitle
+ private class RankingsTitle : OverlayTitle
{
- public readonly Bindable Scope = new Bindable();
-
public RankingsTitle()
{
Title = "ranking";
+ IconTexture = "Icons/rankings";
}
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- Scope.BindValueChanged(scope => Section = scope.NewValue.ToString().ToLowerInvariant(), true);
- }
-
- protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/rankings");
}
}
diff --git a/osu.Game/Overlays/TabControlOverlayHeader.cs b/osu.Game/Overlays/TabControlOverlayHeader.cs
index b199a2a0cf..ab1a6aff78 100644
--- a/osu.Game/Overlays/TabControlOverlayHeader.cs
+++ b/osu.Game/Overlays/TabControlOverlayHeader.cs
@@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
-using osuTK;
namespace osu.Game.Overlays
{
@@ -22,6 +21,7 @@ namespace osu.Game.Overlays
{
protected OsuTabControl TabControl;
+ private readonly Box controlBackground;
private readonly BindableWithCurrent current = new BindableWithCurrent