diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
index 2c89ddc9cf..908b9cb3c6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
@@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
@@ -20,27 +21,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
Origin = Anchor.Centre;
- Masking = true;
- AutoSizeAxes = Axes.Both;
- CornerRadius = width / 2;
- EdgeEffect = new EdgeEffectParameters
+ Child = new SkinnableDrawable("Play/osu/followpoint", _ => new Container
{
- Type = EdgeEffectType.Glow,
- Colour = Color4.White.Opacity(0.2f),
- Radius = 4,
- };
-
- Children = new Drawable[]
- {
- new Box
+ Masking = true,
+ AutoSizeAxes = Axes.Both,
+ CornerRadius = width / 2,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = Color4.White.Opacity(0.2f),
+ Radius = 4,
+ },
+ Child = new Box
{
Size = new Vector2(width),
Blending = BlendingMode.Additive,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Alpha = 0.5f,
- },
- };
+ }
+ }, restrictSize: false);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
index 4ac3b0c983..61a6e6404a 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
@@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Vector2 distanceVector = endPosition - startPosition;
int distance = (int)distanceVector.Length;
- float rotation = (float)Math.Atan2(distanceVector.Y, distanceVector.X);
+ float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI));
double duration = endTime - startTime;
for (int d = (int)(PointDistance * 1.5); d < distance - PointDistance; d += PointDistance)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index 26f3ee6bb4..6bff1380d6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -10,6 +10,7 @@ using OpenTK;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -33,11 +34,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
InternalChildren = new Drawable[]
{
- new SpriteIcon
+ new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.fa_chevron_right
- }
+ }, restrictSize: false)
};
}
@@ -74,6 +75,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
+ private bool hasRotation;
+
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
{
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
@@ -87,15 +90,33 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
int searchStart = isRepeatAtEnd ? curve.Count - 1 : 0;
int direction = isRepeatAtEnd ? -1 : 1;
+ Vector2 aimRotationVector = Vector2.Zero;
+
// find the next vector2 in the curve which is not equal to our current position to infer a rotation.
for (int i = searchStart; i >= 0 && i < curve.Count; i += direction)
{
if (Precision.AlmostEquals(curve[i], Position))
continue;
- Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X));
+ aimRotationVector = curve[i];
break;
}
+
+
+ float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
+ while (Math.Abs(aimRotation - Rotation) > 180)
+ aimRotation += aimRotation < Rotation ? 360 : -360;
+
+ if (!hasRotation)
+ {
+ Rotation = aimRotation;
+ hasRotation = true;
+ }
+ else
+ {
+ // If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
+ Rotation = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 100), Rotation, aimRotation, 0, 50, Easing.OutQuint);
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index db75321eb0..a5ecb63d12 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -8,6 +8,8 @@ using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Skinning;
+using osu.Framework.Graphics.Containers;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -22,23 +24,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{
Size = new Vector2(16) * sliderTick.Scale;
-
- Masking = true;
- CornerRadius = Size.X / 2;
-
Origin = Anchor.Centre;
- BorderThickness = 2;
- BorderColour = Color4.White;
-
InternalChildren = new Drawable[]
{
- new Box
+ new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new Container
{
+ Masking = true,
RelativeSizeAxes = Axes.Both,
- Colour = AccentColour,
- Alpha = 0.3f,
- }
+ Origin = Anchor.Centre,
+ CornerRadius = Size.X / 2,
+
+ BorderThickness = 2,
+ BorderColour = Color4.White,
+
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = AccentColour,
+ Alpha = 0.3f,
+ }
+ }, restrictSize: false)
};
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 92c42af861..182cf66df8 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -10,6 +10,7 @@ using osu.Framework.Input.States;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using OpenTK.Graphics;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -18,6 +19,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private const float width = 128;
private Color4 accentColour = Color4.Black;
+
///
/// The colour that is used for the slider ball.
///
@@ -27,14 +29,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
set
{
accentColour = value;
- if (ball != null)
- ball.Colour = value;
+ if (drawableBall != null)
+ drawableBall.Colour = value;
}
}
private readonly Slider slider;
- public readonly Box FollowCircle;
- private readonly Box ball;
+ public readonly Drawable FollowCircle;
+ private Drawable drawableBall;
public SliderBall(Slider slider)
{
@@ -43,19 +45,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
AutoSizeAxes = Axes.Both;
Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
- BorderThickness = 10;
- BorderColour = Color4.Orange;
- Children = new Drawable[]
+ Children = new[]
{
- FollowCircle = new Box
+ FollowCircle = new Container
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- Colour = Color4.Orange,
Width = width,
Height = width,
Alpha = 0,
+ Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new CircularContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ BorderThickness = 5,
+ BorderColour = Color4.Orange,
+ Blending = BlendingMode.Additive,
+ Child = new Box
+ {
+ Colour = Color4.Orange,
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.2f,
+ }
+ }),
},
new CircularContainer
{
@@ -63,18 +76,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
AutoSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- BorderThickness = 10,
- BorderColour = Color4.White,
Alpha = 1,
- Children = new[]
+ Child = new Container
{
- ball = new Box
+ Width = width,
+ Height = width,
+ // TODO: support skin filename animation (sliderb0, sliderb1...)
+ Child = new SkinnableDrawable("Play/osu/sliderb", _ => new CircularContainer
{
- Colour = AccentColour,
- Alpha = 0.4f,
- Width = width,
- Height = width,
- },
+ Masking = true,
+ RelativeSizeAxes = Axes.Both,
+ BorderThickness = 10,
+ BorderColour = Color4.White,
+ Alpha = 1,
+ Child = drawableBall = new Box
+ {
+ Colour = AccentColour,
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.4f,
+ }
+ }),
}
}
};
@@ -111,6 +132,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
private bool tracking;
+
public bool Tracking
{
get { return tracking; }
@@ -120,8 +142,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
tracking = value;
- FollowCircle.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
- FollowCircle.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
+ FollowCircle.ScaleTo(tracking ? 2f : 1, 300, Easing.OutQuint);
+ FollowCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
index 0b729f0956..3495bc1b4b 100644
--- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
@@ -16,6 +16,9 @@ namespace osu.Game.Rulesets.Osu.Objects
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
+ // Out preempt should be one span early to give the user ample warning.
+ TimePreempt += SpanDuration;
+
// We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders
// we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time.
if (RepeatIndex > 0)
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
index 6d2b37d981..db66c01814 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
@@ -530,7 +530,7 @@ namespace osu.Game.Tests.Visual
{
public new List Items => base.Items;
- public bool PendingFilterTask => FilterTask != null;
+ public bool PendingFilterTask => PendingFilter != null;
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
index 41c45f00f3..b1ffe04b68 100644
--- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
@@ -54,6 +54,8 @@ namespace osu.Game.Tests.Visual
public new BeatmapCarousel Carousel => base.Carousel;
}
+ private TestSongSelect songSelect;
+
protected override void Dispose(bool isDisposing)
{
factory.ResetDatabase();
@@ -63,11 +65,14 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load()
{
- TestSongSelect songSelect = null;
-
factory = new DatabaseContextFactory(LocalStorage);
factory.ResetDatabase();
+ using (var usage = factory.Get())
+ usage.Migrate();
+
+ factory.ResetDatabase();
+
using (var usage = factory.Get())
usage.Migrate();
@@ -77,42 +82,35 @@ namespace osu.Game.Tests.Visual
DefaultBeatmap = defaultBeatmap = Beatmap.Default
});
- void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () =>
- {
- if (deleteMaps)
- {
- manager.Delete(manager.GetAllUsableBeatmapSets());
- Beatmap.SetDefault();
- }
+ Beatmap.SetDefault();
+ }
- if (songSelect != null)
- {
- Remove(songSelect);
- songSelect.Dispose();
- }
-
- Add(songSelect = new TestSongSelect());
- });
-
- loadNewSongSelect(true);
-
- AddWaitStep(3);
+ [SetUp]
+ public virtual void SetUp()
+ {
+ manager?.Delete(manager.GetAllUsableBeatmapSets());
+ Child = songSelect = new TestSongSelect();
+ }
+ [Test]
+ public void TestDummy()
+ {
AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap);
AddAssert("dummy shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap);
- AddStep("import test maps", () =>
- {
- for (int i = 0; i < 100; i += 10)
- manager.Import(createTestBeatmapSet(i));
- });
-
+ addManyTestMaps();
AddWaitStep(3);
+
AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
+ }
- loadNewSongSelect();
+ [Test]
+ public void TestSorting()
+ {
+ addManyTestMaps();
AddWaitStep(3);
+
AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
@@ -121,55 +119,84 @@ namespace osu.Game.Tests.Visual
AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
}
- private BeatmapSetInfo createTestBeatmapSet(int i)
+ [Test]
+ [Ignore("needs fixing")]
+ public void ImportUnderDifferentRuleset()
{
+ changeRuleset(2);
+ importForRuleset(0);
+ AddUntilStep(() => songSelect.Carousel.SelectedBeatmap == null, "no selection");
+ }
+
+ [Test]
+ public void ImportUnderCurrentRuleset()
+ {
+ changeRuleset(2);
+ importForRuleset(2);
+ importForRuleset(1);
+ AddUntilStep(() => songSelect.Carousel.SelectedBeatmap.RulesetID == 2, "has selection");
+
+ changeRuleset(1);
+ AddUntilStep(() => songSelect.Carousel.SelectedBeatmap.RulesetID == 1, "has selection");
+
+ changeRuleset(0);
+ AddUntilStep(() => songSelect.Carousel.SelectedBeatmap == null, "no selection");
+ }
+
+ private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())));
+
+ private static int importId;
+ private int getImportId() => ++importId;
+
+ private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
+
+ private void addManyTestMaps()
+ {
+ AddStep("import test maps", () =>
+ {
+ var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
+
+ for (int i = 0; i < 100; i += 10)
+ manager.Import(createTestBeatmapSet(i, usableRulesets));
+ });
+ }
+
+ private BeatmapSetInfo createTestBeatmapSet(int setId, RulesetInfo[] rulesets)
+ {
+ int j = 0;
+ RulesetInfo getRuleset() => rulesets[j++ % rulesets.Length];
+
+ var beatmaps = new List();
+
+ for (int i = 0; i < 6; i++)
+ {
+ int beatmapId = setId * 10 + i;
+
+ beatmaps.Add(new BeatmapInfo
+ {
+ Ruleset = getRuleset(),
+ OnlineBeatmapID = beatmapId,
+ Path = "normal.osu",
+ Version = $"{beatmapId}",
+ BaseDifficulty = new BeatmapDifficulty
+ {
+ OverallDifficulty = 3.5f,
+ }
+ });
+ }
+
return new BeatmapSetInfo
{
- OnlineBeatmapSetID = 1234 + i,
+ OnlineBeatmapSetID = setId,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
// Create random metadata, then we can check if sorting works based on these
- Artist = "MONACA " + RNG.Next(0, 9),
- Title = "Black Song " + RNG.Next(0, 9),
+ Artist = "Some Artist " + RNG.Next(0, 9),
+ Title = $"Some Song (set id {setId})",
AuthorString = "Some Guy " + RNG.Next(0, 9),
},
- Beatmaps = new List(new[]
- {
- new BeatmapInfo
- {
- OnlineBeatmapID = 1234 + i,
- Ruleset = rulesets.AvailableRulesets.First(),
- Path = "normal.osu",
- Version = "Normal",
- BaseDifficulty = new BeatmapDifficulty
- {
- OverallDifficulty = 3.5f,
- }
- },
- new BeatmapInfo
- {
- OnlineBeatmapID = 1235 + i,
- Ruleset = rulesets.AvailableRulesets.First(),
- Path = "hard.osu",
- Version = "Hard",
- BaseDifficulty = new BeatmapDifficulty
- {
- OverallDifficulty = 5,
- }
- },
- new BeatmapInfo
- {
- OnlineBeatmapID = 1236 + i,
- Ruleset = rulesets.AvailableRulesets.First(),
- Path = "insane.osu",
- Version = "Insane",
- BaseDifficulty = new BeatmapDifficulty
- {
- OverallDifficulty = 7,
- }
- },
- }),
+ Beatmaps = beatmaps
};
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index cfd34d44e7..6c906bb1e4 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -134,6 +134,8 @@ namespace osu.Game.Beatmaps
return converted;
}
+ public override string ToString() => BeatmapInfo.ToString();
+
public bool BackgroundLoaded => background.IsResultAvailable;
public Texture Background => background.Value.Result;
public async Task GetBackgroundAsync() => await background.Value;
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 83912944d4..025d5f50e3 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -197,7 +197,13 @@ namespace osu.Game
return;
}
- Beatmap.Value = BeatmapManager.GetWorkingBeatmap(beatmap.Beatmaps.First());
+ var databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
+
+ // Use first beatmap available for current ruleset, else switch ruleset.
+ var first = databasedSet.Beatmaps.FirstOrDefault(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
+
+ ruleset.Value = first.Ruleset;
+ Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
}
switch (currentScreen)
diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs
index 7758e171c5..99a5881487 100644
--- a/osu.Game/Overlays/Direct/DownloadButton.cs
+++ b/osu.Game/Overlays/Direct/DownloadButton.cs
@@ -14,6 +14,7 @@ namespace osu.Game.Overlays.Direct
{
public class DownloadButton : OsuAnimatedButton
{
+ private readonly BeatmapSetInfo beatmapSet;
private readonly SpriteIcon icon;
private readonly SpriteIcon checkmark;
private readonly BeatmapSetDownloader downloader;
@@ -21,11 +22,13 @@ namespace osu.Game.Overlays.Direct
private OsuColour colours;
- public DownloadButton(BeatmapSetInfo set, bool noVideo = false)
+ public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
{
+ this.beatmapSet = beatmapSet;
+
AddRange(new Drawable[]
{
- downloader = new BeatmapSetDownloader(set, noVideo),
+ downloader = new BeatmapSetDownloader(beatmapSet, noVideo),
background = new Box
{
RelativeSizeAxes = Axes.Both,
@@ -47,26 +50,6 @@ namespace osu.Game.Overlays.Direct
Icon = FontAwesome.fa_check,
}
});
-
- Action = () =>
- {
- if (downloader.DownloadState == BeatmapSetDownloader.DownloadStatus.Downloading)
- {
- // todo: replace with ShakeContainer after https://github.com/ppy/osu/pull/2909 is merged.
- Content.MoveToX(-5, 50, Easing.OutSine).Then()
- .MoveToX(5, 100, Easing.InOutSine).Then()
- .MoveToX(-5, 100, Easing.InOutSine).Then()
- .MoveToX(0, 50, Easing.InSine);
- }
- else if (downloader.DownloadState == BeatmapSetDownloader.DownloadStatus.Downloaded)
- {
- // TODO: Jump to song select with this set when the capability is implemented
- }
- else
- {
- downloader.Download();
- }
- };
}
protected override void LoadComplete()
@@ -77,9 +60,29 @@ namespace osu.Game.Overlays.Direct
}
[BackgroundDependencyLoader(permitNulls: true)]
- private void load(OsuColour colours)
+ private void load(OsuColour colours, OsuGame game)
{
this.colours = colours;
+
+ Action = () =>
+ {
+ switch (downloader.DownloadState.Value)
+ {
+ case BeatmapSetDownloader.DownloadStatus.Downloading:
+ // todo: replace with ShakeContainer after https://github.com/ppy/osu/pull/2909 is merged.
+ Content.MoveToX(-5, 50, Easing.OutSine).Then()
+ .MoveToX(5, 100, Easing.InOutSine).Then()
+ .MoveToX(-5, 100, Easing.InOutSine).Then()
+ .MoveToX(0, 50, Easing.InSine);
+ break;
+ case BeatmapSetDownloader.DownloadStatus.Downloaded:
+ game.PresentBeatmap(beatmapSet);
+ break;
+ default:
+ downloader.Download();
+ break;
+ }
+ };
}
private void updateState(BeatmapSetDownloader.DownloadStatus state)
diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs
index 10463fd961..a6a311f6eb 100644
--- a/osu.Game/Rulesets/RulesetInfo.cs
+++ b/osu.Game/Rulesets/RulesetInfo.cs
@@ -25,5 +25,7 @@ namespace osu.Game.Rulesets
public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this);
public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo;
+
+ public override string ToString() => $"{Name} ({ShortName}) ID: {ID}";
}
}
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index f1721d8941..6e4454a311 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -330,13 +330,13 @@ namespace osu.Game.Screens.Select
private FilterCriteria activeCriteria = new FilterCriteria();
- protected ScheduledDelegate FilterTask;
+ protected ScheduledDelegate PendingFilter;
public bool AllowSelection = true;
public void FlushPendingFilterOperations()
{
- if (FilterTask?.Completed == false)
+ if (PendingFilter?.Completed == false)
{
applyActiveCriteria(false, false);
Update();
@@ -357,18 +357,18 @@ namespace osu.Game.Screens.Select
void perform()
{
- FilterTask = null;
+ PendingFilter = null;
root.Filter(activeCriteria);
itemsCache.Invalidate();
if (scroll) scrollPositionCache.Invalidate();
}
- FilterTask?.Cancel();
- FilterTask = null;
+ PendingFilter?.Cancel();
+ PendingFilter = null;
if (debounce)
- FilterTask = Scheduler.AddDelayed(perform, 250);
+ PendingFilter = Scheduler.AddDelayed(perform, 250);
else
perform();
}
diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs
index 274b859e82..9ba8b085f3 100644
--- a/osu.Game/Screens/Select/FilterControl.cs
+++ b/osu.Game/Screens/Select/FilterControl.cs
@@ -182,7 +182,7 @@ namespace osu.Game.Screens.Select
showConverted.ValueChanged += val => updateCriteria();
ruleset.BindTo(parentRuleset);
- ruleset.BindValueChanged(val => updateCriteria(), true);
+ ruleset.BindValueChanged(_ => updateCriteria(), true);
}
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 3cf406fabb..1bcd65e30b 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -12,6 +12,7 @@ using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Logging;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
using osu.Framework.Screens;
@@ -199,10 +200,6 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader(true)]
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours)
{
- // manual binding to parent ruleset to allow for delayed load in the incoming direction.
- base.Ruleset.ValueChanged += r => updateSelectedBeatmap(beatmapNoDebounce);
- Ruleset.ValueChanged += r => base.Ruleset.Value = r;
-
if (Footer != null)
{
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
@@ -225,15 +222,6 @@ namespace osu.Game.Screens.Select
sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand");
Carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSetsEnumerable();
-
- Beatmap.BindDisabledChanged(disabled => Carousel.AllowSelection = !disabled, true);
- Beatmap.BindValueChanged(workingBeatmapChanged);
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- base.Ruleset.ValueChanged += r => updateSelectedBeatmap(beatmapNoDebounce);
}
public void Edit(BeatmapInfo beatmap)
@@ -296,59 +284,86 @@ namespace osu.Game.Screens.Select
private BeatmapInfo beatmapNoDebounce;
private RulesetInfo rulesetNoDebounce;
+ private void updateSelectedBeatmap(BeatmapInfo beatmap)
+ {
+ if (beatmap?.Equals(beatmapNoDebounce) == true)
+ return;
+
+ beatmapNoDebounce = beatmap;
+ performUpdateSelected();
+ }
+
+ private void updateSelectedRuleset(RulesetInfo ruleset)
+ {
+ if (ruleset?.Equals(rulesetNoDebounce) == true)
+ return;
+
+ rulesetNoDebounce = ruleset;
+ performUpdateSelected();
+ }
+
///
/// selection has been changed as the result of a user interaction.
///
- private void updateSelectedBeatmap(BeatmapInfo beatmap)
+ private void performUpdateSelected()
{
- var ruleset = base.Ruleset.Value;
+ var beatmap = beatmapNoDebounce;
+ var ruleset = rulesetNoDebounce;
- void performLoad()
+ void run()
{
+ Logger.Log($"updating selection with beatmap:{beatmap?.ID.ToString() ?? "null"} ruleset:{ruleset?.ID.ToString() ?? "null"}");
+
WorkingBeatmap working = Beatmap.Value;
+
bool preview = false;
+ if (ruleset?.Equals(Ruleset.Value) == false)
+ {
+ Logger.Log($"ruleset changed from \"{Ruleset.Value}\" to \"{ruleset}\"");
+ Ruleset.Value = ruleset;
+
+ // force a filter before attempting to change the beatmap.
+ // we may still be in the wrong ruleset as there is a debounce delay on ruleset changes.
+ Carousel.Filter(null, false);
+
+ // Filtering only completes after the carousel runs Update.
+ // If we also have a pending beatmap change we should delay it one frame.
+ selectionChangedDebounce = Schedule(run);
+ return;
+ }
+
// We may be arriving here due to another component changing the bindable Beatmap.
// In these cases, the other component has already loaded the beatmap, so we don't need to do so again.
- if (beatmap?.Equals(Beatmap.Value.BeatmapInfo) != true)
+ if (!Equals(beatmap, Beatmap.Value.BeatmapInfo))
{
+ Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\"");
+
preview = beatmap?.BeatmapSetInfoID != Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID;
working = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
+
+ if (beatmap != null)
+ {
+ if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID)
+ sampleChangeDifficulty.Play();
+ else
+ sampleChangeBeatmap.Play();
+ }
}
-
working.Mods.Value = Enumerable.Empty();
-
Beatmap.Value = working;
- Ruleset.Value = ruleset;
ensurePlayingSelected(preview);
-
UpdateBeatmap(Beatmap.Value);
}
- if (beatmap?.Equals(beatmapNoDebounce) == true && ruleset?.Equals(rulesetNoDebounce) == true)
- return;
-
selectionChangedDebounce?.Cancel();
- beatmapNoDebounce = beatmap;
- rulesetNoDebounce = ruleset;
-
if (beatmap == null)
- performLoad();
+ run();
else
- {
- if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID)
- sampleChangeDifficulty.Play();
- else
- sampleChangeBeatmap.Play();
-
- if (beatmap == Beatmap.Value.BeatmapInfo)
- performLoad();
- else
- selectionChangedDebounce = Scheduler.AddDelayed(performLoad, 200);
- }
+ selectionChangedDebounce = Scheduler.AddDelayed(run, 200);
}
private void triggerRandom()
@@ -464,6 +479,8 @@ namespace osu.Game.Screens.Select
/// The working beatmap.
protected virtual void UpdateBeatmap(WorkingBeatmap beatmap)
{
+ Logger.Log($"working beatmap updated to {beatmap}");
+
if (Background is BackgroundScreenBeatmap backgroundModeBeatmap)
{
backgroundModeBeatmap.Beatmap = beatmap;
@@ -495,6 +512,17 @@ namespace osu.Game.Screens.Select
private void carouselBeatmapsLoaded()
{
+ if (rulesetNoDebounce == null)
+ {
+ // manual binding to parent ruleset to allow for delayed load in the incoming direction.
+ rulesetNoDebounce = Ruleset.Value = base.Ruleset.Value;
+ base.Ruleset.ValueChanged += updateSelectedRuleset;
+ Ruleset.ValueChanged += r => base.Ruleset.Value = r;
+
+ Beatmap.BindDisabledChanged(disabled => Carousel.AllowSelection = !disabled, true);
+ Beatmap.BindValueChanged(workingBeatmapChanged);
+ }
+
if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false
&& Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false))
return;
@@ -503,7 +531,7 @@ namespace osu.Game.Screens.Select
{
// in the case random selection failed, we want to trigger selectionChanged
// to show the dummy beatmap (we have nothing else to display).
- updateSelectedBeatmap(null);
+ performUpdateSelected();
}
}