diff --git a/osu.Android.props b/osu.Android.props
index 4167d07698..969eb205e0 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,7 +51,6 @@
osu.licenseheader
-
@@ -63,6 +62,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs
index 4ea1f22006..6c8515eb90 100644
--- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs
+++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs
@@ -37,9 +37,6 @@ namespace osu.Game.Rulesets.Catch.Replays
float lastPosition = 0.5f;
double lastTime = 0;
- // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
- addFrame(-100000, lastPosition);
-
void moveToNext(CatchHitObject h)
{
float positionChange = Math.Abs(lastPosition - h.X);
@@ -127,6 +124,10 @@ namespace osu.Game.Rulesets.Catch.Replays
private void addFrame(double time, float? position = null, bool dashing = false)
{
+ // todo: can be removed once FramedReplayInputHandler correctly handles rewinding before first frame.
+ if (Replay.Frames.Count == 0)
+ Replay.Frames.Add(new CatchReplayFrame(time - 1, position, false, null));
+
var last = currentFrame;
currentFrame = new CatchReplayFrame(time, position, dashing, last);
Replay.Frames.Add(currentFrame);
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs
index f260357df5..a5248c7712 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs
@@ -16,6 +16,11 @@ namespace osu.Game.Rulesets.Mania.Tests
[HeadlessTest]
public class TestSceneAutoGeneration : OsuTestScene
{
+ ///
+ /// The number of frames which are generated at the start of a replay regardless of hitobject content.
+ ///
+ private const int frame_offset = 1;
+
[Test]
public void TestSingleNote()
{
@@ -28,11 +33,11 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
- Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
+ Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released");
}
[Test]
@@ -49,11 +54,11 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released");
}
[Test]
@@ -69,11 +74,11 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
- Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
+ Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
}
[Test]
@@ -91,11 +96,13 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
+
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
}
[Test]
@@ -112,15 +119,15 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
- Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time");
- Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time");
- Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
- Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
+ Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect first note release time");
+ Assert.AreEqual(2000, generated.Frames[frame_offset + 2].Time, "Incorrect second note hit time");
+ Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released");
}
[Test]
@@ -139,16 +146,16 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time");
- Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time");
- Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
- Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released");
- Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released");
- Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time");
+ Assert.AreEqual(2000, generated.Frames[frame_offset + 1].Time, "Incorrect second note hit time");
+ Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key1), "Key1 has not been released");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has been released");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 3], ManiaAction.Key2), "Key2 has not been released");
}
[Test]
@@ -166,14 +173,14 @@ namespace osu.Game.Rulesets.Mania.Tests
var generated = new ManiaAutoGenerator(beatmap).Generate();
- Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames");
- Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
- Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time");
- Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
- Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed");
- Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released");
+ Assert.IsTrue(generated.Frames.Count == frame_offset + 3, "Replay must have 3 generated frames");
+ Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
+ Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect second note press time + first note release time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect second note release time");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
+ Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key2), "Key2 has not been pressed");
+ Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key2), "Key2 has not been released");
}
private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action));
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
index 7b8bbc2095..2b336ca16d 100644
--- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
@@ -47,9 +47,6 @@ namespace osu.Game.Rulesets.Mania.Replays
public override Replay Generate()
{
- // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
- Replay.Frames.Add(new ManiaReplayFrame(-100000, 0));
-
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
var actions = new List();
@@ -70,6 +67,10 @@ namespace osu.Game.Rulesets.Mania.Replays
}
}
+ // todo: can be removed once FramedReplayInputHandler correctly handles rewinding before first frame.
+ if (Replay.Frames.Count == 0)
+ Replay.Frames.Add(new ManiaReplayFrame(group.First().Time - 1));
+
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs
index 399cf22599..95c2810e94 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs
@@ -29,7 +29,8 @@ namespace osu.Game.Rulesets.Osu.Tests
};
for (int i = 0; i < 512; i++)
- beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 });
+ if (i % 32 < 20)
+ beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 });
return beatmap;
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
index 2d940479f3..32c9e913c6 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
@@ -17,7 +17,9 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Description => @"Play with no approach circles and fading circles/sliders.";
public override double ScoreMultiplier => 1.06;
- public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn) };
+
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable), typeof(OsuModSpinIn) };
+
private const double fade_in_duration_multiplier = 0.4;
private const double fade_out_duration_multiplier = 0.3;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
index 62b5ecfd58..e786ec86f9 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => 1;
// todo: this mod should be able to be compatible with hidden with a bit of further implementation.
- public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween), typeof(OsuModHidden) };
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween), typeof(OsuModHidden), typeof(OsuModTraceable) };
private const int rotate_offset = 360;
private const float rotate_starting_width = 2;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
new file mode 100644
index 0000000000..7e20feba02
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.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 System.Linq;
+using osu.Framework.Bindables;
+using System.Collections.Generic;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Osu.Mods
+{
+ internal class OsuModTraceable : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
+ {
+ public override string Name => "Traceable";
+ public override string Acronym => "TC";
+ public override IconUsage Icon => FontAwesome.Brands.SnapchatGhost;
+ public override ModType Type => ModType.Fun;
+ public override string Description => "Put your faith in the approach circles...";
+ public override double ScoreMultiplier => 1;
+
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModeObjectScaleTween) };
+ private Bindable increaseFirstObjectVisibility = new Bindable();
+
+ public void ReadFromConfig(OsuConfigManager config)
+ {
+ increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility);
+ }
+
+ public void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
+ drawable.ApplyCustomUpdateState += ApplyTraceableState;
+ }
+
+ protected void ApplyTraceableState(DrawableHitObject drawable, ArmedState state)
+ {
+ if (!(drawable is DrawableOsuHitObject drawableOsu))
+ return;
+
+ var h = drawableOsu.HitObject;
+
+ switch (drawable)
+ {
+ case DrawableHitCircle circle:
+ // we only want to see the approach circle
+ using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
+ circle.CirclePiece.Hide();
+
+ break;
+
+ case DrawableSlider slider:
+ slider.AccentColour.BindValueChanged(_ =>
+ {
+ //will trigger on skin change.
+ slider.Body.AccentColour = slider.AccentColour.Value.Opacity(0);
+ slider.Body.BorderColour = slider.AccentColour.Value;
+ }, true);
+
+ break;
+
+ case DrawableSpinner spinner:
+ spinner.Disc.Hide();
+ spinner.Background.Hide();
+ break;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs
index e926ade41b..923278f484 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Mods
private Bindable increaseFirstObjectVisibility = new Bindable();
- public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn) };
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn), typeof(OsuModTraceable) };
public void ReadFromConfig(OsuConfigManager config)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 83646c561d..c90f230f93 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
{
- public ApproachCircle ApproachCircle;
+ public ApproachCircle ApproachCircle { get; }
private readonly IBindable positionBindable = new Bindable();
private readonly IBindable stackHeightBindable = new Bindable();
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly HitArea hitArea;
- private readonly SkinnableDrawable mainContent;
+ public SkinnableDrawable CirclePiece { get; }
public DrawableHitCircle(HitCircle h)
: base(h)
@@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return true;
},
},
- mainContent = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.HitCircle), _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)),
+ CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.HitCircle), _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)),
ApproachCircle = new ApproachCircle
{
Alpha = 0,
@@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdateInitialTransforms();
- mainContent.FadeInFromZero(HitObject.TimeFadeIn);
+ CirclePiece.FadeInFromZero(HitObject.TimeFadeIn);
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
ApproachCircle.ScaleTo(1f, HitObject.TimePreempt);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 08b43b0345..643a0f7336 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -163,9 +163,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private float sliderPathRadius;
- protected override void SkinChanged(ISkinSource skin, bool allowFallback)
+ protected override void ApplySkin(ISkinSource skin, bool allowFallback)
{
- base.SkinChanged(skin, allowFallback);
+ base.ApplySkin(skin, allowFallback);
Body.BorderSize = skin.GetConfig(OsuSkinConfiguration.SliderBorderSize)?.Value ?? SliderBody.DEFAULT_BORDER_SIZE;
sliderPathRadius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index df2ae81a5a..fa69cec78d 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -139,6 +139,7 @@ namespace osu.Game.Rulesets.Osu
new OsuModSpinIn(),
new MultiMod(new OsuModGrow(), new OsuModDeflate()),
new MultiMod(new ModWindUp(), new ModWindDown()),
+ new OsuModTraceable(),
};
case ModType.System:
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
index 452ac859de..f94071a7a9 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
@@ -3,6 +3,7 @@
using System.ComponentModel;
using System.Linq;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
@@ -12,6 +13,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Description("Player instantiated with an autoplay mod.")]
public class TestSceneAutoplay : AllPlayersTestScene
{
+ private ClockBackedTestWorkingBeatmap.TrackVirtualManual track;
+
protected override Player CreatePlayer(Ruleset ruleset)
{
Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
@@ -21,7 +24,18 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override void AddCheckSteps()
{
AddUntilStep("score above zero", () => ((ScoreAccessiblePlayer)Player).ScoreProcessor.TotalScore.Value > 0);
- AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0));
+ AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
+ AddStep("rewind", () => track.Seek(-10000));
+ AddUntilStep("key counter reset", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
+ }
+
+ protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
+ {
+ var working = base.CreateWorkingBeatmap(beatmap);
+
+ track = (ClockBackedTestWorkingBeatmap.TrackVirtualManual)working.Track;
+
+ return working;
}
private class ScoreAccessiblePlayer : TestPlayer
@@ -29,6 +43,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
+ public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
+
public ScoreAccessiblePlayer()
: base(false, false)
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
index d57ec44f39..cca6301b02 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
@@ -6,8 +6,6 @@ using System.Linq;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.UI;
-using osu.Game.Scoring;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay
@@ -17,9 +15,7 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override Player CreatePlayer(Ruleset ruleset)
{
Mods.Value = Array.Empty();
-
- var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty());
- return new FailPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap));
+ return new FailPlayer();
}
protected override void AddCheckSteps()
@@ -29,16 +25,12 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("total judgements == 1", () => ((FailPlayer)Player).ScoreProcessor.JudgedHits == 1);
}
- private class FailPlayer : ReplayPlayer
+ private class FailPlayer : TestPlayer
{
- public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
-
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
- protected override bool PauseOnFocusLost => false;
-
- public FailPlayer(Score score)
- : base(score, false, false)
+ public FailPlayer()
+ : base(false, false)
{
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
index 237fee1594..ffc025a942 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
@@ -14,6 +14,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osuTK;
@@ -47,9 +48,11 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for track to start running", () => track.IsRunning);
addSeekStep(3000);
AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
+ AddUntilStep("key counter counted keys", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7));
AddStep("clear results", () => player.AppliedResults.Clear());
addSeekStep(0);
AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
+ AddUntilStep("key counters reset", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
AddAssert("no results triggered", () => player.AppliedResults.Count == 0);
}
@@ -90,6 +93,10 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public readonly List AppliedResults = new List();
+ public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
+
+ public new HUDOverlay HUDOverlay => base.HUDOverlay;
+
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
index 18088a9a5b..ad747e88e1 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
@@ -7,7 +7,6 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
-using osu.Framework.Timing;
using osu.Game.Screens.Play;
using osuTK.Input;
@@ -25,14 +24,15 @@ namespace osu.Game.Tests.Visual.Gameplay
public TestSceneKeyCounter()
{
- KeyCounterKeyboard rewindTestKeyCounterKeyboard;
+ KeyCounterKeyboard testCounter;
+
KeyCounterDisplay kc = new KeyCounterDisplay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new KeyCounter[]
{
- rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X),
+ testCounter = new KeyCounterKeyboard(Key.X),
new KeyCounterKeyboard(Key.X),
new KeyCounterMouse(MouseButton.Left),
new KeyCounterMouse(MouseButton.Right),
@@ -44,10 +44,8 @@ namespace osu.Game.Tests.Visual.Gameplay
Key key = (Key)((int)Key.A + RNG.Next(26));
kc.Add(new KeyCounterKeyboard(key));
});
- AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v);
Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
- double time1 = 0;
AddStep($"Press {testKey} key", () =>
{
@@ -55,48 +53,17 @@ namespace osu.Game.Tests.Visual.Gameplay
InputManager.ReleaseKey(testKey);
});
- AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
+ AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 1);
AddStep($"Press {testKey} key", () =>
{
InputManager.PressKey(testKey);
InputManager.ReleaseKey(testKey);
- time1 = Clock.CurrentTime;
});
- AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 2);
-
- IFrameBasedClock oldClock = null;
-
- AddStep($"Rewind {testKey} counter once", () =>
- {
- oldClock = rewindTestKeyCounterKeyboard.Clock;
- rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(time1 - 10));
- });
-
- AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
-
- AddStep($"Rewind {testKey} counter to zero", () => rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(0)));
-
- AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 0);
-
- AddStep("Restore clock", () => rewindTestKeyCounterKeyboard.Clock = oldClock);
+ AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 2);
Add(kc);
}
-
- private class FixedClock : IClock
- {
- private readonly double time;
-
- public FixedClock(double time)
- {
- this.time = time;
- }
-
- public double CurrentTime => time;
- public double Rate => 1;
- public bool IsRunning => false;
- }
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs
index 3fbce9d43c..36335bc54a 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs
@@ -26,12 +26,14 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddUntilStep("score above zero", () => ((ScoreAccessibleReplayPlayer)Player).ScoreProcessor.TotalScore.Value > 0);
AddUntilStep("key counter counted keys", () => ((ScoreAccessibleReplayPlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0));
+ AddAssert("cannot fail", () => !((ScoreAccessibleReplayPlayer)Player).AllowFail);
}
private class ScoreAccessibleReplayPlayer : ReplayPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
+ public new bool AllowFail => base.AllowFail;
protected override bool PauseOnFocusLost => false;
diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs
index 539601c359..5b528c5ab2 100644
--- a/osu.Game/Overlays/Music/PlaylistList.cs
+++ b/osu.Game/Overlays/Music/PlaylistList.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
@@ -18,7 +19,6 @@ namespace osu.Game.Overlays.Music
public class PlaylistList : CompositeDrawable
{
public Action Selected;
- public Action OrderChanged;
private readonly ItemsScrollContainer items;
@@ -28,7 +28,6 @@ namespace osu.Game.Overlays.Music
{
RelativeSizeAxes = Axes.Both,
Selected = set => Selected?.Invoke(set),
- OrderChanged = (s, i) => OrderChanged?.Invoke(s, i)
};
}
@@ -45,13 +44,17 @@ namespace osu.Game.Overlays.Music
private class ItemsScrollContainer : OsuScrollContainer
{
public Action Selected;
- public Action OrderChanged;
private readonly SearchContainer search;
private readonly FillFlowContainer items;
private readonly IBindable beatmapBacking = new Bindable();
+ private IBindableList beatmaps;
+
+ [Resolved]
+ private MusicController musicController { get; set; }
+
public ItemsScrollContainer()
{
Children = new Drawable[]
@@ -73,27 +76,35 @@ namespace osu.Game.Overlays.Music
}
[BackgroundDependencyLoader]
- private void load(BeatmapManager beatmaps, IBindable beatmap)
+ private void load(IBindable beatmap)
{
- beatmaps.GetAllUsableBeatmapSets().ForEach(addBeatmapSet);
- beatmaps.ItemAdded += addBeatmapSet;
- beatmaps.ItemRemoved += removeBeatmapSet;
+ beatmaps = musicController.BeatmapSets.GetBoundCopy();
+ beatmaps.ItemsAdded += i => i.ForEach(addBeatmapSet);
+ beatmaps.ItemsRemoved += i => i.ForEach(removeBeatmapSet);
+ beatmaps.ForEach(addBeatmapSet);
beatmapBacking.BindTo(beatmap);
beatmapBacking.ValueChanged += _ => updateSelectedSet();
}
- private void addBeatmapSet(BeatmapSetInfo obj) => Schedule(() =>
+ private void addBeatmapSet(BeatmapSetInfo obj)
{
- items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) });
- });
+ if (obj == draggedItem?.BeatmapSetInfo) return;
- private void removeBeatmapSet(BeatmapSetInfo obj) => Schedule(() =>
+ Schedule(() => items.Insert(items.Count - 1, new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) }));
+ }
+
+ private void removeBeatmapSet(BeatmapSetInfo obj)
{
- var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == obj.ID);
- if (itemToRemove != null)
- items.Remove(itemToRemove);
- });
+ if (obj == draggedItem?.BeatmapSetInfo) return;
+
+ Schedule(() =>
+ {
+ var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == obj.ID);
+ if (itemToRemove != null)
+ items.Remove(itemToRemove);
+ });
+ }
private void updateSelectedSet()
{
@@ -112,6 +123,8 @@ namespace osu.Game.Overlays.Music
private Vector2 nativeDragPosition;
private PlaylistItem draggedItem;
+ private int? dragDestination;
+
protected override bool OnDragStart(DragStartEvent e)
{
nativeDragPosition = e.ScreenSpaceMousePosition;
@@ -131,10 +144,17 @@ namespace osu.Game.Overlays.Music
protected override bool OnDragEnd(DragEndEvent e)
{
nativeDragPosition = e.ScreenSpaceMousePosition;
- var handled = draggedItem != null || base.OnDragEnd(e);
- draggedItem = null;
- return handled;
+ if (draggedItem == null)
+ return base.OnDragEnd(e);
+
+ if (dragDestination != null)
+ musicController.ChangeBeatmapSetPosition(draggedItem.BeatmapSetInfo, dragDestination.Value);
+
+ draggedItem = null;
+ dragDestination = null;
+
+ return true;
}
protected override void Update()
@@ -210,7 +230,7 @@ namespace osu.Game.Overlays.Music
}
items.SetLayoutPosition(draggedItem, dstIndex);
- OrderChanged?.Invoke(draggedItem.BeatmapSetInfo, dstIndex);
+ dragDestination = dstIndex;
}
private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren
diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs
index ec3d708645..ae81a6c117 100644
--- a/osu.Game/Overlays/Music/PlaylistOverlay.cs
+++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs
@@ -1,7 +1,6 @@
// 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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -22,12 +21,6 @@ namespace osu.Game.Overlays.Music
private const float transition_duration = 600;
private const float playlist_height = 510;
- ///
- /// Invoked when the order of an item in the list has changed.
- /// The second parameter indicates the new index of the item.
- ///
- public Action OrderChanged;
-
private readonly Bindable beatmap = new Bindable();
private BeatmapManager beatmaps;
@@ -65,7 +58,6 @@ namespace osu.Game.Overlays.Music
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 95, Bottom = 10, Right = 10 },
Selected = itemSelected,
- OrderChanged = (s, i) => OrderChanged?.Invoke(s, i)
},
filter = new FilterControl
{
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index 6ad147735b..db94b0278f 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
+using osu.Framework.MathUtils;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Input.Bindings;
@@ -24,7 +25,9 @@ namespace osu.Game.Overlays
[Resolved]
private BeatmapManager beatmaps { get; set; }
- private List beatmapSets;
+ public IBindableList BeatmapSets => beatmapSets;
+
+ private readonly BindableList beatmapSets = new BindableList();
public bool IsUserPaused { get; private set; }
@@ -46,7 +49,7 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader]
private void load()
{
- beatmapSets = beatmaps.GetAllUsableBeatmapSets();
+ beatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets().OrderBy(_ => RNG.Next()));
beatmaps.ItemAdded += handleBeatmapAdded;
beatmaps.ItemRemoved += handleBeatmapRemoved;
}
@@ -140,7 +143,7 @@ namespace osu.Game.Overlays
{
queuedDirection = TrackChangeDirection.Prev;
- var playable = beatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? beatmapSets.LastOrDefault();
+ var playable = BeatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? BeatmapSets.LastOrDefault();
if (playable != null)
{
@@ -165,7 +168,7 @@ namespace osu.Game.Overlays
if (!instant)
queuedDirection = TrackChangeDirection.Next;
- var playable = beatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? beatmapSets.FirstOrDefault();
+ var playable = BeatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? BeatmapSets.FirstOrDefault();
if (playable != null)
{
@@ -200,8 +203,8 @@ namespace osu.Game.Overlays
else
{
//figure out the best direction based on order in playlist.
- var last = beatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
- var next = beatmap.NewValue == null ? -1 : beatmapSets.TakeWhile(b => b.ID != beatmap.NewValue.BeatmapSetInfo?.ID).Count();
+ var last = BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
+ var next = beatmap.NewValue == null ? -1 : BeatmapSets.TakeWhile(b => b.ID != beatmap.NewValue.BeatmapSetInfo?.ID).Count();
direction = last > next ? TrackChangeDirection.Prev : TrackChangeDirection.Next;
}
diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs
index cf42c8005a..6b79f2af07 100644
--- a/osu.Game/Overlays/NowPlayingOverlay.cs
+++ b/osu.Game/Overlays/NowPlayingOverlay.cs
@@ -81,7 +81,6 @@ namespace osu.Game.Overlays
{
RelativeSizeAxes = Axes.X,
Y = player_height + 10,
- OrderChanged = musicController.ChangeBeatmapSetPosition
},
playerContainer = new Container
{
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
index 5189d573cc..ea2811e5cd 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
@@ -31,11 +31,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
LabelText = "Hit Lighting",
Bindable = config.GetBindable(OsuSetting.HitLighting)
},
- new SettingsCheckbox
- {
- LabelText = "Rotate cursor when dragging",
- Bindable = config.GetBindable(OsuSetting.CursorRotation)
- },
new SettingsEnumDropdown
{
LabelText = "Screenshot format",
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs
similarity index 71%
rename from osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs
rename to osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs
index 92f64d0e14..dd822fedb6 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/MainMenuSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs
@@ -6,7 +6,7 @@ using osu.Game.Configuration;
namespace osu.Game.Overlays.Settings.Sections.Graphics
{
- public class MainMenuSettings : SettingsSubsection
+ public class UserInterfaceSettings : SettingsSubsection
{
protected override string Header => "User Interface";
@@ -15,6 +15,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{
Children = new[]
{
+ new SettingsCheckbox
+ {
+ LabelText = "Rotate cursor when dragging",
+ Bindable = config.GetBindable(OsuSetting.CursorRotation)
+ },
new SettingsCheckbox
{
LabelText = "Parallax",
diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs
index 3d6086d3ea..89caa3dc8f 100644
--- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections
new RendererSettings(),
new LayoutSettings(),
new DetailSettings(),
- new MainMenuSettings(),
+ new UserInterfaceSettings(),
};
}
}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 00b57f7249..b94de0df89 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -240,7 +240,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
#endregion
- protected override void SkinChanged(ISkinSource skin, bool allowFallback)
+ protected sealed override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
@@ -250,6 +250,20 @@ namespace osu.Game.Rulesets.Objects.Drawables
AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White;
}
+
+ ApplySkin(skin, allowFallback);
+
+ if (IsLoaded)
+ updateState(State.Value, true);
+ }
+
+ ///
+ /// Called when a change is made to the skin.
+ ///
+ /// The new skin.
+ /// Whether fallback to default skin should be allowed if the custom skin is missing this resource.
+ protected virtual void ApplySkin(ISkinSource skin, bool allowFallback)
+ {
}
///
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index e25c3bd0e7..98e27240d3 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -137,9 +137,9 @@ namespace osu.Game.Rulesets.UI
{
}
- public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action));
+ public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.ElapsedFrameTime > 0));
- public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action));
+ public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action, Clock.ElapsedFrameTime > 0));
}
#endregion
diff --git a/osu.Game/Screens/Play/GameplayClock.cs b/osu.Game/Screens/Play/GameplayClock.cs
index b1948d02d5..379c4c89ed 100644
--- a/osu.Game/Screens/Play/GameplayClock.cs
+++ b/osu.Game/Screens/Play/GameplayClock.cs
@@ -42,5 +42,7 @@ namespace osu.Game.Screens.Play
public double FramesPerSecond => underlyingClock.FramesPerSecond;
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
+
+ public IClock Source => underlyingClock;
}
}
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index eee7235a6e..0f9edf5606 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -231,7 +231,6 @@ namespace osu.Game.Screens.Play
protected virtual KeyCounterDisplay CreateKeyCounter() => new KeyCounterDisplay
{
- FadeTime = 50,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding(10),
diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs
index 88a62ac8d4..f4109a63d0 100644
--- a/osu.Game/Screens/Play/KeyCounter.cs
+++ b/osu.Game/Screens/Play/KeyCounter.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -22,9 +20,6 @@ namespace osu.Game.Screens.Play
private Container textLayer;
private SpriteText countSpriteText;
- private readonly List states = new List();
- private KeyCounterState currentState;
-
public bool IsCounting { get; set; } = true;
private int countPresses;
@@ -52,20 +47,30 @@ namespace osu.Game.Screens.Play
{
isLit = value;
updateGlowSprite(value);
-
- if (value && IsCounting)
- {
- CountPresses++;
- saveState();
- }
}
}
}
+ public void Increment()
+ {
+ if (!IsCounting)
+ return;
+
+ CountPresses++;
+ }
+
+ public void Decrement()
+ {
+ if (!IsCounting)
+ return;
+
+ CountPresses--;
+ }
+
//further: change default values here and in KeyCounterCollection if needed, instead of passing them in every constructor
public Color4 KeyDownTextColor { get; set; } = Color4.DarkGray;
public Color4 KeyUpTextColor { get; set; } = Color4.White;
- public int FadeTime { get; set; }
+ public double FadeTime { get; set; }
protected KeyCounter(string name)
{
@@ -73,11 +78,8 @@ namespace osu.Game.Screens.Play
}
[BackgroundDependencyLoader(true)]
- private void load(TextureStore textures, GameplayClock clock)
+ private void load(TextureStore textures)
{
- if (clock != null)
- Clock = clock;
-
Children = new Drawable[]
{
buttonSprite = new Sprite
@@ -132,42 +134,16 @@ namespace osu.Game.Screens.Play
{
if (show)
{
- glowSprite.FadeIn(FadeTime);
- textLayer.FadeColour(KeyDownTextColor, FadeTime);
+ double remainingFadeTime = FadeTime * (1 - glowSprite.Alpha);
+ glowSprite.FadeIn(remainingFadeTime, Easing.OutQuint);
+ textLayer.FadeColour(KeyDownTextColor, remainingFadeTime, Easing.OutQuint);
}
else
{
- glowSprite.FadeOut(FadeTime);
- textLayer.FadeColour(KeyUpTextColor, FadeTime);
+ double remainingFadeTime = 8 * FadeTime * glowSprite.Alpha;
+ glowSprite.FadeOut(remainingFadeTime, Easing.OutQuint);
+ textLayer.FadeColour(KeyUpTextColor, remainingFadeTime, Easing.OutQuint);
}
}
-
- public void ResetCount()
- {
- CountPresses = 0;
- states.Clear();
- }
-
- protected override void Update()
- {
- base.Update();
-
- if (currentState?.Time > Clock.CurrentTime)
- restoreStateTo(Clock.CurrentTime);
- }
-
- private void saveState()
- {
- if (currentState == null || currentState.Time < Clock.CurrentTime)
- states.Add(currentState = new KeyCounterState(Clock.CurrentTime, CountPresses));
- }
-
- private void restoreStateTo(double time)
- {
- states.RemoveAll(state => state.Time > time);
-
- currentState = states.LastOrDefault();
- CountPresses = currentState?.Count ?? 0;
- }
}
}
diff --git a/osu.Game/Screens/Play/KeyCounterAction.cs b/osu.Game/Screens/Play/KeyCounterAction.cs
index 8deac653ad..f60ad7aa5a 100644
--- a/osu.Game/Screens/Play/KeyCounterAction.cs
+++ b/osu.Game/Screens/Play/KeyCounterAction.cs
@@ -1,11 +1,9 @@
// 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.Input.Bindings;
-
namespace osu.Game.Screens.Play
{
- public class KeyCounterAction : KeyCounter, IKeyBindingHandler
+ public class KeyCounterAction : KeyCounter
where T : struct
{
public T Action { get; }
@@ -16,15 +14,25 @@ namespace osu.Game.Screens.Play
Action = action;
}
- public bool OnPressed(T action)
+ public bool OnPressed(T action, bool forwards)
{
- if (action.Equals(Action)) IsLit = true;
+ if (!action.Equals(Action))
+ return false;
+
+ IsLit = true;
+ if (forwards)
+ Increment();
return false;
}
- public bool OnReleased(T action)
+ public bool OnReleased(T action, bool forwards)
{
- if (action.Equals(Action)) IsLit = false;
+ if (!action.Equals(Action))
+ return false;
+
+ IsLit = false;
+ if (!forwards)
+ Decrement();
return false;
}
}
diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs
index d5967f5899..1edb95ca46 100644
--- a/osu.Game/Screens/Play/KeyCounterDisplay.cs
+++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs
@@ -17,6 +17,7 @@ namespace osu.Game.Screens.Play
public class KeyCounterDisplay : FillFlowContainer
{
private const int duration = 100;
+ private const double key_fade_time = 80;
public readonly Bindable Visible = new Bindable(true);
private readonly Bindable configVisibility = new Bindable();
@@ -33,17 +34,11 @@ namespace osu.Game.Screens.Play
base.Add(key);
key.IsCounting = IsCounting;
- key.FadeTime = FadeTime;
+ key.FadeTime = key_fade_time;
key.KeyDownTextColor = KeyDownTextColor;
key.KeyUpTextColor = KeyUpTextColor;
}
- public void ResetCount()
- {
- foreach (var counter in Children)
- counter.ResetCount();
- }
-
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
@@ -68,22 +63,6 @@ namespace osu.Game.Screens.Play
}
}
- private int fadeTime;
-
- public int FadeTime
- {
- get => fadeTime;
- set
- {
- if (value != fadeTime)
- {
- fadeTime = value;
- foreach (var child in Children)
- child.FadeTime = value;
- }
- }
- }
-
private Color4 keyDownTextColor = Color4.DarkGray;
public Color4 KeyDownTextColor
@@ -123,11 +102,6 @@ namespace osu.Game.Screens.Play
private Receptor receptor;
- public Receptor GetReceptor()
- {
- return receptor ?? (receptor = new Receptor(this));
- }
-
public void SetReceptor(Receptor receptor)
{
if (this.receptor != null)
diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboard.cs
index d9b6dca79d..29188b6b59 100644
--- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs
+++ b/osu.Game/Screens/Play/KeyCounterKeyboard.cs
@@ -18,7 +18,12 @@ namespace osu.Game.Screens.Play
protected override bool OnKeyDown(KeyDownEvent e)
{
- if (e.Key == Key) IsLit = true;
+ if (e.Key == Key)
+ {
+ IsLit = true;
+ Increment();
+ }
+
return base.OnKeyDown(e);
}
diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs
index 95fa58e5c0..828441de6e 100644
--- a/osu.Game/Screens/Play/KeyCounterMouse.cs
+++ b/osu.Game/Screens/Play/KeyCounterMouse.cs
@@ -36,7 +36,12 @@ namespace osu.Game.Screens.Play
protected override bool OnMouseDown(MouseDownEvent e)
{
- if (e.Button == Button) IsLit = true;
+ if (e.Button == Button)
+ {
+ IsLit = true;
+ Increment();
+ }
+
return base.OnMouseDown(e);
}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 309f4837e5..4b234ab296 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -86,6 +86,12 @@ namespace osu.Game.Screens.Play
[Cached(Type = typeof(IBindable>))]
protected new readonly Bindable> Mods = new Bindable>(Array.Empty());
+ ///
+ /// Whether failing should be allowed.
+ /// By default, this checks whether all selected mods allow failing.
+ ///
+ protected virtual bool AllowFail => Mods.Value.OfType().All(m => m.AllowFail);
+
private readonly bool allowPause;
private readonly bool showResults;
@@ -360,7 +366,7 @@ namespace osu.Game.Screens.Play
private bool onFail()
{
- if (Mods.Value.OfType().Any(m => !m.AllowFail))
+ if (!AllowFail)
return false;
HasFailed = true;
diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs
index a9c0ee3a15..b040549efc 100644
--- a/osu.Game/Screens/Play/ReplayPlayer.cs
+++ b/osu.Game/Screens/Play/ReplayPlayer.cs
@@ -9,6 +9,9 @@ namespace osu.Game.Screens.Play
{
private readonly Score score;
+ // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108)
+ protected override bool AllowFail => false;
+
public ReplayPlayer(Score score, bool allowPause = true, bool showResults = true)
: base(allowPause, showResults)
{
diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs
index 98f158c725..4b6eea6b6e 100644
--- a/osu.Game/Skinning/DefaultLegacySkin.cs
+++ b/osu.Game/Skinning/DefaultLegacySkin.cs
@@ -13,6 +13,13 @@ namespace osu.Game.Skinning
: base(Info, storage, audioManager, string.Empty)
{
Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
+ Configuration.ComboColours.AddRange(new[]
+ {
+ new Color4(255, 192, 0, 255),
+ new Color4(0, 202, 0, 255),
+ new Color4(18, 124, 255, 255),
+ new Color4(242, 24, 57, 255),
+ });
}
public static SkinInfo Info { get; } = new SkinInfo
diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs
index 2b8baab57c..8e98d51962 100644
--- a/osu.Game/Tests/Visual/OsuTestScene.cs
+++ b/osu.Game/Tests/Visual/OsuTestScene.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 5703293caf..a733a0e7f9 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 683dccf3ae..4bfa1ebcd0 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -118,8 +118,8 @@
-
-
+
+