diff --git a/osu.Android.props b/osu.Android.props
index 98f9bf1a42..b9451fc744 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -63,6 +63,6 @@
-
+
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
index e55dc1f902..693faee3b7 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
@@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Osu.Tests
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
[TestCase(6.931145117263422, "diffcalc-test")]
+ [TestCase(1.0736587013228804d, "zero-length-sliders")]
public void Test(double expected, string name)
=> base.Test(expected, name);
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
index 7f6a60c400..fe11ead94d 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
this.hitCircle = hitCircle;
Origin = Anchor.Centre;
- Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2);
+ Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Scale = new Vector2(hitCircle.Scale);
CornerRadius = Size.X / 2;
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
index 957550a051..f1f55731b6 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
@@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
InternalChild = body = new ManualSliderBody
{
AccentColour = Color4.Transparent,
- PathRadius = slider.Scale * 64
};
}
@@ -34,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
body.BorderColour = colours.Yellow;
PositionBindable.BindValueChanged(_ => updatePosition(), true);
- ScaleBindable.BindValueChanged(scale => body.PathRadius = scale.NewValue * 64, true);
+ ScaleBindable.BindValueChanged(scale => body.PathRadius = scale.NewValue * OsuHitObject.OBJECT_RADIUS, true);
}
private void updatePosition() => Position = slider.StackedPosition;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index f372cb65ce..4533e08a2b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -29,6 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ShakeDuration = 30,
RelativeSizeAxes = Axes.Both
});
+
Alpha = 0;
}
@@ -38,6 +39,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren);
protected override bool RemoveInternal(Drawable drawable) => shakeContainer.Remove(drawable);
+ protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
+
protected sealed override void UpdateState(ArmedState state)
{
double transformTime = HitObject.StartTime - HitObject.TimePreempt;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 05cb42d853..56b5decd30 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
Body = new SnakingSliderBody(s)
{
- PathRadius = s.Scale * 64,
+ PathRadius = s.Scale * OsuHitObject.OBJECT_RADIUS,
},
ticks = new Container { RelativeSizeAxes = Axes.Both },
repeatPoints = new Container { RelativeSizeAxes = Axes.Both },
@@ -156,6 +156,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
+ public override void OnKilled()
+ {
+ base.OnKilled();
+ Body.RecyclePath();
+ }
+
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
index 786cac7198..dc0b149140 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public CirclePiece()
{
- Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2);
+ Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Masking = true;
CornerRadius = Size.X / 2;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs
index b960f40578..8ff16f8b84 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ExplodePiece.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public ExplodePiece()
{
- Size = new Vector2(128);
+ Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
index 8e5eb886aa..c22073f56c 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/FlashPiece.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 osu.Framework.Graphics;
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public FlashPiece()
{
- Size = new Vector2(128);
+ Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs
index 28180a7f71..575f2c92c5 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.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 osu.Framework.Graphics;
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public RingPiece()
{
- Size = new Vector2(128);
+ Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 7d1d77ae96..9ba8ad3474 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition
{
- private const float width = 128;
-
private Color4 accentColour = Color4.Black;
public Func GetInitialHitAction;
@@ -57,8 +55,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- Width = width,
- Height = width,
+ Width = OsuHitObject.OBJECT_RADIUS * 2,
+ Height = OsuHitObject.OBJECT_RADIUS * 2,
Alpha = 0,
Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new CircularContainer
{
@@ -84,8 +82,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Alpha = 1,
Child = new Container
{
- Width = width,
- Height = width,
+ Width = OsuHitObject.OBJECT_RADIUS * 2,
+ Height = OsuHitObject.OBJECT_RADIUS * 2,
// TODO: support skin filename animation (sliderb0, sliderb1...)
Child = new SkinnableDrawable("Play/osu/sliderb", _ => new CircularContainer
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
index 33b3667c4f..97c7c9cec5 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -13,7 +13,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public const float DEFAULT_BORDER_SIZE = 1;
- private readonly SliderPath path;
+ private SliderPath path;
+
protected Path Path => path;
public float PathRadius
@@ -77,6 +78,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
InternalChild = path = new SliderPath();
}
+ ///
+ /// Initialises a new , releasing all resources retained by the old one.
+ ///
+ public void RecyclePath()
+ {
+ InternalChild = path = new SliderPath
+ {
+ Position = path.Position,
+ PathRadius = path.PathRadius,
+ AccentColour = path.AccentColour,
+ BorderColour = path.BorderColour,
+ BorderSize = path.BorderSize,
+ Vertices = path.Vertices
+ };
+ }
+
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
///
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 364c182dd4..d1221fd2d3 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects
{
public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition
{
- public const double OBJECT_RADIUS = 64;
+ public const float OBJECT_RADIUS = 64;
public double TimePreempt = 600;
public double TimeFadeIn = 400;
diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/zero-length-sliders.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/zero-length-sliders.osu
new file mode 100644
index 0000000000..18736043b5
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/zero-length-sliders.osu
@@ -0,0 +1,28 @@
+osu file format v14
+
+[Difficulty]
+HPDrainRate:3
+CircleSize:3
+OverallDifficulty:3
+ApproachRate:4.5
+SliderMultiplier:0.799999999999999
+SliderTickRate:1
+
+[TimingPoints]
+800,260.869565217391,3,2,10,60,1,0
+
+[HitObjects]
+// Linear
+78,193,2365,2,0,L|330:193,1,0
+78,193,3669,2,0,L|330:193,1,0
+78,193,4973,2,0,L|330:193,1,0
+
+// Perfect-curve
+151,206,6278,2,0,P|293:75|345:204,1,0
+151,206,8104,2,0,P|293:75|345:204,1,0
+151,206,9930,2,0,P|293:75|345:204,1,0
+
+// Bezier
+76,191,11756,2,0,B|176:59|358:340|438:190,1,0
+76,191,13582,2,0,B|176:59|358:340|438:190,1,0
+76,191,15408,2,0,B|176:59|358:340|438:190,1,0
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index b986076593..05eb0ffdbf 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly TrailPart[] parts = new TrailPart[max_sprites];
private Vector2 size;
- private readonly VertexBatch vertexBatch = new QuadBatch(max_sprites, 1);
+ private readonly TrailBatch vertexBatch = new TrailBatch(max_sprites, 1);
public TrailDrawNode(CursorTrail source)
: base(source)
@@ -196,21 +196,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
for (int i = 0; i < parts.Length; ++i)
{
+ vertexBatch.DrawTime = parts[i].Time;
+
Vector2 pos = parts[i].Position;
- float localTime = parts[i].Time;
DrawQuad(
texture,
new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
DrawColourInfo.Colour,
null,
- v => vertexBatch.Add(new TexturedTrailVertex
- {
- Position = v.Position,
- TexturePosition = v.TexturePosition,
- Time = localTime + 1,
- Colour = v.Colour,
- }));
+ vertexBatch.AddAction);
}
shader.Unbind();
@@ -222,6 +217,25 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
vertexBatch.Dispose();
}
+
+ // Todo: This shouldn't exist, but is currently used to reduce allocations by caching variable-capturing closures.
+ private class TrailBatch : QuadBatch
+ {
+ public new readonly Action AddAction;
+ public float DrawTime;
+
+ public TrailBatch(int size, int maxBuffers)
+ : base(size, maxBuffers)
+ {
+ AddAction = v => Add(new TexturedTrailVertex
+ {
+ Position = v.Position,
+ TexturePosition = v.TexturePosition,
+ Time = DrawTime + 1,
+ Colour = v.Colour,
+ });
+ }
+ }
}
[StructLayout(LayoutKind.Sequential)]
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index 0cbe0cca85..9037faf606 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -12,6 +12,7 @@ using osu.Game.Rulesets.UI;
using System.Linq;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.UI.Cursor;
+using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.UI
{
@@ -39,7 +40,13 @@ namespace osu.Game.Rulesets.Osu.UI
RelativeSizeAxes = Axes.Both,
Depth = 1,
},
- HitObjectContainer,
+ // Todo: This should not exist, but currently helps to reduce LOH allocations due to unbinding skin source events on judgement disposal
+ // Todo: Remove when hitobjects are properly pooled
+ new LocalSkinOverrideContainer(null)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = HitObjectContainer,
+ },
approachCircles = new ApproachCircleProxyContainer
{
RelativeSizeAxes = Axes.Both,
diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
index 883f0c5e3f..838347800f 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Online
{
typeof(HistoricalSection),
typeof(PaginatedMostPlayedBeatmapContainer),
- typeof(DrawableMostPlayedRow),
+ typeof(DrawableMostPlayedBeatmap),
typeof(DrawableProfileRow)
};
diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
index ea3f0b61b9..df3a45d1cc 100644
--- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
@@ -66,6 +66,11 @@ namespace osu.Game.Beatmaps
///
public int FavouriteCount { get; set; }
+ ///
+ /// Whether this beatmap set has been favourited by the current user.
+ ///
+ public bool HasFavourited { get; set; }
+
///
/// The availability of this beatmap set.
///
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 37aa0024da..949a2aab6f 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -247,7 +247,7 @@ namespace osu.Game.Beatmaps
// cancelling the beatmap load is safe for now since the retrieval is a synchronous
// operation. if we add an async retrieval method this may need to be reconsidered.
- beatmapCancellation.Cancel();
+ beatmapCancellation?.Cancel();
total_count.Value--;
}
diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs
index 526b3da8a6..d13475189d 100644
--- a/osu.Game/Graphics/Backgrounds/Background.cs
+++ b/osu.Game/Graphics/Backgrounds/Background.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Graphics.Backgrounds
///
public class Background : CompositeDrawable
{
- public Sprite Sprite;
+ public readonly Sprite Sprite;
private readonly string textureName;
@@ -51,7 +51,7 @@ namespace osu.Game.Graphics.Backgrounds
/// A to which further transforms can be added.
public void BlurTo(Vector2 newBlurSigma, double duration = 0, Easing easing = Easing.None)
{
- if (bufferedContainer == null)
+ if (bufferedContainer == null && newBlurSigma != Vector2.Zero)
{
RemoveInternal(Sprite);
@@ -63,7 +63,7 @@ namespace osu.Game.Graphics.Backgrounds
});
}
- bufferedContainer.BlurTo(newBlurSigma, duration, easing);
+ bufferedContainer?.BlurTo(newBlurSigma, duration, easing);
}
}
}
diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs
index 29113e0e2f..2b68e8530d 100644
--- a/osu.Game/Graphics/Backgrounds/Triangles.cs
+++ b/osu.Game/Graphics/Backgrounds/Triangles.cs
@@ -8,7 +8,6 @@ using osuTK.Graphics;
using System;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
-using osuTK.Graphics.ES30;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Allocation;
@@ -137,11 +136,13 @@ namespace osu.Game.Graphics.Backgrounds
}
}
+ protected int AimCount;
+
private void addTriangles(bool randomY)
{
- int aimTriangleCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio);
+ AimCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio);
- for (int i = 0; i < aimTriangleCount - parts.Count; i++)
+ for (int i = 0; i < AimCount - parts.Count; i++)
parts.Add(createTriangle(randomY));
}
@@ -190,7 +191,7 @@ namespace osu.Game.Graphics.Backgrounds
private readonly List parts = new List();
private Vector2 size;
- private readonly LinearBatch vertexBatch = new LinearBatch(100 * 3, 10, PrimitiveType.Triangles);
+ private TriangleBatch vertexBatch;
public TrianglesDrawNode(Triangles source)
: base(source)
@@ -213,6 +214,12 @@ namespace osu.Game.Graphics.Backgrounds
{
base.Draw(vertexAction);
+ if (Source.AimCount > 0 && (vertexBatch == null || vertexBatch.Size != Source.AimCount))
+ {
+ vertexBatch?.Dispose();
+ vertexBatch = new TriangleBatch(Source.AimCount, 1);
+ }
+
shader.Bind();
Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy;
@@ -246,7 +253,7 @@ namespace osu.Game.Graphics.Backgrounds
{
base.Dispose(isDisposing);
- vertexBatch.Dispose();
+ vertexBatch?.Dispose();
}
}
diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs
new file mode 100644
index 0000000000..74e387d60e
--- /dev/null
+++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.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.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osuTK;
+
+namespace osu.Game.Graphics.Sprites
+{
+ public class GlowingSpriteText : Container, IHasText
+ {
+ private readonly OsuSpriteText spriteText, blurredText;
+
+ public string Text
+ {
+ get => spriteText.Text;
+ set => blurredText.Text = spriteText.Text = value;
+ }
+
+ public FontUsage Font
+ {
+ get => spriteText.Font;
+ set => blurredText.Font = spriteText.Font = value.With(fixedWidth: true);
+ }
+
+ public Vector2 TextSize
+ {
+ get => spriteText.Size;
+ set => blurredText.Size = spriteText.Size = value;
+ }
+
+ public ColourInfo TextColour
+ {
+ get => spriteText.Colour;
+ set => spriteText.Colour = value;
+ }
+
+ public ColourInfo GlowColour
+ {
+ get => blurredText.Colour;
+ set => blurredText.Colour = value;
+ }
+
+ public GlowingSpriteText()
+ {
+ AutoSizeAxes = Axes.Both;
+
+ Children = new Drawable[]
+ {
+ new BufferedContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ BlurSigma = new Vector2(4),
+ CacheDrawnFrameBuffer = true,
+ RelativeSizeAxes = Axes.Both,
+ Blending = BlendingMode.Additive,
+ Size = new Vector2(3f),
+ Children = new[]
+ {
+ blurredText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Shadow = false,
+ },
+ },
+ },
+ spriteText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Shadow = false,
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs
index 45d751f00e..f3384163b8 100644
--- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs
@@ -7,21 +7,20 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
- public class GetUserBeatmapsRequest : APIRequest>
+ public class GetUserBeatmapsRequest : PaginatedAPIRequest>
{
private readonly long userId;
- private readonly int offset;
+
private readonly BeatmapSetType type;
- public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0)
+ public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page = 0, int itemsPerPage = 6)
+ : base(page, itemsPerPage)
{
this.userId = userId;
- this.offset = offset;
this.type = type;
}
- // ReSharper disable once ImpureMethodCallOnReadonlyValueField
- protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}";
+ protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}";
}
public enum BeatmapSetType
diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs
index 40e52bdaf6..9f094e51c4 100644
--- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs
@@ -6,17 +6,16 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
- public class GetUserMostPlayedBeatmapsRequest : APIRequest>
+ public class GetUserMostPlayedBeatmapsRequest : PaginatedAPIRequest>
{
private readonly long userId;
- private readonly int offset;
- public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0)
+ public GetUserMostPlayedBeatmapsRequest(long userId, int page = 0, int itemsPerPage = 5)
+ : base(page, itemsPerPage)
{
this.userId = userId;
- this.offset = offset;
}
- protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}";
+ protected override string Target => $@"users/{userId}/beatmapsets/most_played";
}
}
diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs
index 9f80180e70..4908e5ecc2 100644
--- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs
@@ -6,18 +6,17 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
- public class GetUserRecentActivitiesRequest : APIRequest>
+ public class GetUserRecentActivitiesRequest : PaginatedAPIRequest>
{
private readonly long userId;
- private readonly int offset;
- public GetUserRecentActivitiesRequest(long userId, int offset = 0)
+ public GetUserRecentActivitiesRequest(long userId, int page = 0, int itemsPerPage = 5)
+ : base(page, itemsPerPage)
{
this.userId = userId;
- this.offset = offset;
}
- protected override string Target => $"users/{userId}/recent_activity?offset={offset}";
+ protected override string Target => $"users/{userId}/recent_activity";
}
public enum RecentActivityType
diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
index 48a43bbbad..d41966fe1b 100644
--- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
@@ -6,21 +6,19 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
- public class GetUserScoresRequest : APIRequest>
+ public class GetUserScoresRequest : PaginatedAPIRequest>
{
private readonly long userId;
private readonly ScoreType type;
- private readonly int offset;
- public GetUserScoresRequest(long userId, ScoreType type, int offset = 0)
+ public GetUserScoresRequest(long userId, ScoreType type, int page = 0, int itemsPerPage = 5)
+ : base(page, itemsPerPage)
{
this.userId = userId;
this.type = type;
- this.offset = offset;
}
- // ReSharper disable once ImpureMethodCallOnReadonlyValueField
- protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}?offset={offset}";
+ protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}";
}
public enum ScoreType
diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs
new file mode 100644
index 0000000000..52e12f04ee
--- /dev/null
+++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.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 System.Globalization;
+using osu.Framework.IO.Network;
+
+namespace osu.Game.Online.API.Requests
+{
+ public abstract class PaginatedAPIRequest : APIRequest
+ {
+ private readonly int page;
+ private readonly int itemsPerPage;
+
+ protected PaginatedAPIRequest(int page, int itemsPerPage)
+ {
+ this.page = page;
+ this.itemsPerPage = itemsPerPage;
+ }
+
+ protected override WebRequest CreateWebRequest()
+ {
+ var req = base.CreateWebRequest();
+
+ req.AddParameter("offset", (page * itemsPerPage).ToString(CultureInfo.InvariantCulture));
+ req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture));
+
+ return req;
+ }
+ }
+}
diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs
index 200a705500..e5bfde8f8f 100644
--- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs
@@ -30,6 +30,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"preview_url")]
private string preview { get; set; }
+ [JsonProperty(@"has_favourited")]
+ private bool hasFavourited { get; set; }
+
[JsonProperty(@"play_count")]
private int playCount { get; set; }
@@ -91,6 +94,7 @@ namespace osu.Game.Online.API.Requests.Responses
Ranked = ranked,
LastUpdated = lastUpdated,
Availability = availability,
+ HasFavourited = hasFavourited,
},
Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(),
};
diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs
index 35f7ba1c1b..98f15599fc 100644
--- a/osu.Game/Online/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Online/Leaderboards/Leaderboard.cs
@@ -51,7 +51,6 @@ namespace osu.Game.Online.Leaderboards
loading.Hide();
- // schedule because we may not be loaded yet (LoadComponentAsync complains).
showScoresDelegate?.Cancel();
showScoresCancellationSource?.Cancel();
@@ -61,28 +60,22 @@ namespace osu.Game.Online.Leaderboards
// ensure placeholder is hidden when displaying scores
PlaceholderState = PlaceholderState.Successful;
- scrollFlow = CreateScoreFlow();
- scrollFlow.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1));
+ var sf = CreateScoreFlow();
+ sf.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1));
- if (!IsLoaded)
- showScoresDelegate = Schedule(showScores);
- else
- showScores();
-
- void showScores() => LoadComponentAsync(scrollFlow, _ =>
+ // schedule because we may not be loaded yet (LoadComponentAsync complains).
+ showScoresDelegate = Schedule(() => LoadComponentAsync(sf, _ =>
{
- scrollContainer.Add(scrollFlow);
+ scrollContainer.Add(scrollFlow = sf);
int i = 0;
foreach (var s in scrollFlow.Children)
- {
using (s.BeginDelayedSequence(i++ * 50, true))
s.Show();
- }
scrollContainer.ScrollTo(0f, false);
- }, (showScoresCancellationSource = new CancellationTokenSource()).Token);
+ }, (showScoresCancellationSource = new CancellationTokenSource()).Token));
}
}
@@ -201,13 +194,17 @@ namespace osu.Game.Online.Leaderboards
private APIRequest getScoresRequest;
+ protected abstract bool IsOnlineScope { get; }
+
public void APIStateChanged(IAPIProvider api, APIState state)
{
switch (state)
{
case APIState.Online:
case APIState.Offline:
- UpdateScores();
+ if (IsOnlineScope)
+ UpdateScores();
+
break;
}
}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 9840b59805..008f8208eb 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -187,7 +187,13 @@ namespace osu.Game.Online.Leaderboards
Spacing = new Vector2(5f, 0f),
Children = new Drawable[]
{
- scoreLabel = new GlowingSpriteText(score.TotalScore.ToString(@"N0"), OsuFont.Numeric.With(size: 23), Color4.White, OsuColour.FromHex(@"83ccfa")),
+ scoreLabel = new GlowingSpriteText
+ {
+ TextColour = Color4.White,
+ GlowColour = OsuColour.FromHex(@"83ccfa"),
+ Text = score.TotalScore.ToString(@"N0"),
+ Font = OsuFont.Numeric.With(size: 23),
+ },
RankContainer = new Container
{
Size = new Vector2(40f, 20f),
@@ -275,49 +281,6 @@ namespace osu.Game.Online.Leaderboards
base.OnHoverLost(e);
}
- private class GlowingSpriteText : Container
- {
- public GlowingSpriteText(string text, FontUsage font, Color4 textColour, Color4 glowColour)
- {
- AutoSizeAxes = Axes.Both;
-
- Children = new Drawable[]
- {
- new BufferedContainer
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- BlurSigma = new Vector2(4),
- CacheDrawnFrameBuffer = true,
- RelativeSizeAxes = Axes.Both,
- Blending = BlendingMode.Additive,
- Size = new Vector2(3f),
- Children = new[]
- {
- new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Font = font.With(fixedWidth: true),
- Text = text,
- Colour = glowColour,
- Shadow = false,
- },
- },
- },
- new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Font = font.With(fixedWidth: true),
- Text = text,
- Colour = textColour,
- Shadow = false,
- },
- };
- }
- }
-
private class ScoreComponentLabel : Container, IHasTooltip
{
private const float icon_size = 20;
@@ -367,10 +330,14 @@ namespace osu.Game.Online.Leaderboards
},
},
},
- new GlowingSpriteText(statistic.Value, OsuFont.GetFont(size: 17, weight: FontWeight.Bold), Color4.White, OsuColour.FromHex(@"83ccfa"))
+ new GlowingSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
+ TextColour = Color4.White,
+ GlowColour = OsuColour.FromHex(@"83ccfa"),
+ Text = statistic.Value,
+ Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold),
},
},
};
diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs
index 7207739646..11f56bc163 100644
--- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osuTK;
@@ -15,7 +16,9 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
{
public class FavouriteButton : HeaderButton
{
- public readonly Bindable Favourited = new Bindable();
+ public readonly Bindable BeatmapSet = new Bindable();
+
+ private readonly Bindable favourited = new Bindable();
[BackgroundDependencyLoader]
private void load()
@@ -54,7 +57,15 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
},
});
- Favourited.ValueChanged += favourited =>
+ BeatmapSet.BindValueChanged(setInfo =>
+ {
+ if (setInfo.NewValue?.OnlineInfo?.HasFavourited == null)
+ return;
+
+ favourited.Value = setInfo.NewValue.OnlineInfo.HasFavourited;
+ });
+
+ favourited.ValueChanged += favourited =>
{
if (favourited.NewValue)
{
@@ -67,8 +78,6 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
icon.Icon = FontAwesome.Regular.Heart;
}
};
-
- Action = () => Favourited.Value = !Favourited.Value;
}
protected override void UpdateAfterChildren()
diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs
index b50eac2c1a..260a989628 100644
--- a/osu.Game/Overlays/BeatmapSet/Header.cs
+++ b/osu.Game/Overlays/BeatmapSet/Header.cs
@@ -161,7 +161,10 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Top = 10 },
Children = new Drawable[]
{
- favouriteButton = new FavouriteButton(),
+ favouriteButton = new FavouriteButton
+ {
+ BeatmapSet = { BindTarget = BeatmapSet }
+ },
downloadButtonsContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs
index 22d7ea9c97..a6cc2b0500 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs
@@ -19,7 +19,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
public class ScoresContainer : CompositeDrawable
{
private const int spacing = 15;
- private const int fade_duration = 200;
private readonly Box background;
private readonly ScoreTable scoreTable;
@@ -53,8 +52,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
Schedule(() =>
{
- loading = false;
-
topScoresContainer.Clear();
if (value?.Scores.Any() != true)
@@ -128,11 +125,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
background.Colour = colours.Gray2;
}
- private bool loading
- {
- set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
- }
-
private void getScores(BeatmapInfo beatmap)
{
getScoresRequest?.Cancel();
@@ -141,15 +133,16 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Scores = null;
if (beatmap?.OnlineBeatmapID.HasValue != true)
- {
- loading = false;
return;
- }
+ loadingAnimation.Show();
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
- getScoresRequest.Success += scores => Scores = scores;
+ getScoresRequest.Success += scores =>
+ {
+ loadingAnimation.Hide();
+ Scores = scores;
+ };
api.Queue(getScoresRequest);
- loading = true;
}
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs
index a15d3c5fd1..ffc39e5af2 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs
@@ -62,7 +62,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
},
}
},
- avatar = new UpdateableAvatar(hideImmediately: true)
+ avatar = new UpdateableAvatar
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -99,7 +99,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold)
},
- flag = new UpdateableFlag(hideImmediately: true)
+ flag = new UpdateableFlag
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
diff --git a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs
index fdab9f1b90..106aaa616b 100644
--- a/osu.Game/Overlays/Direct/DirectRulesetSelector.cs
+++ b/osu.Game/Overlays/Direct/DirectRulesetSelector.cs
@@ -26,8 +26,13 @@ namespace osu.Game.Overlays.Direct
TabContainer.Masking = false;
TabContainer.Spacing = new Vector2(10, 0);
AutoSizeAxes = Axes.Both;
+ }
- Current.DisabledChanged += value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint);
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Current.BindDisabledChanged(value => SelectedTab.FadeColour(value ? Color4.DarkGray : Color4.White, 200, Easing.OutQuint), true);
}
protected override TabItem CreateTabItem(RulesetInfo value) => new DirectRulesetTabItem(value);
diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs
index 16326900f1..13b547eed3 100644
--- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs
+++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs
@@ -4,26 +4,23 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Localisation;
using osu.Game.Beatmaps;
-using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
-using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.Profile.Sections
{
///
/// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row (see ).
///
- public class BeatmapMetadataContainer : OsuHoverContainer
+ public abstract class BeatmapMetadataContainer : OsuHoverContainer
{
private readonly BeatmapInfo beatmap;
- public BeatmapMetadataContainer(BeatmapInfo beatmap)
+ protected BeatmapMetadataContainer(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
+
AutoSizeAxes = Axes.Both;
- TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}";
}
[BackgroundDependencyLoader(true)]
@@ -40,23 +37,10 @@ namespace osu.Game.Overlays.Profile.Sections
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- new OsuSpriteText
- {
- Text = new LocalisedString((
- $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ",
- $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")),
- Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold, italics: true)
- },
- new OsuSpriteText
- {
- Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)),
- Padding = new MarginPadding { Top = 3 },
- Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular, italics: true)
- },
- },
+ Children = CreateText(beatmap),
};
}
+
+ protected abstract Drawable[] CreateText(BeatmapInfo beatmap);
}
}
diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs
index b6b0e605d7..1b6c1c99a6 100644
--- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs
+++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
protected override void ShowMore()
{
- request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
+ request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage);
request.Success += sets => Schedule(() =>
{
MoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0);
diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs
new file mode 100644
index 0000000000..0206c4e13b
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs
@@ -0,0 +1,182 @@
+// 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 osu.Framework.Graphics.Sprites;
+using osu.Framework.Localisation;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osuTK;
+using System.Collections.Generic;
+using osu.Framework.Graphics.Cursor;
+
+namespace osu.Game.Overlays.Profile.Sections.Historical
+{
+ public class DrawableMostPlayedBeatmap : OsuHoverContainer
+ {
+ private const int cover_width = 100;
+ private const int corner_radius = 6;
+ private const int height = 50;
+
+ private readonly BeatmapInfo beatmap;
+ private readonly int playCount;
+
+ private Box background;
+
+ protected override IEnumerable EffectTargets => new[] { background };
+
+ public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount)
+ {
+ this.beatmap = beatmap;
+ this.playCount = playCount;
+ Enabled.Value = true; //manually enabled, because we have no action
+
+ RelativeSizeAxes = Axes.X;
+ Height = height;
+
+ Masking = true;
+ CornerRadius = corner_radius;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ IdleColour = colours.GreySeafoam;
+ HoverColour = colours.GreySeafoamLight;
+
+ Children = new Drawable[]
+ {
+ new UpdateableBeatmapSetCover
+ {
+ RelativeSizeAxes = Axes.Y,
+ Width = cover_width,
+ BeatmapSet = beatmap.BeatmapSet,
+ CoverType = BeatmapSetCoverType.List,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = cover_width - corner_radius },
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ CornerRadius = corner_radius,
+ Children = new Drawable[]
+ {
+ background = new Box { RelativeSizeAxes = Axes.Both },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding(10),
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new MostPlayedBeatmapMetadataContainer(beatmap),
+ new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular))
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Colour = colours.GreySeafoamLighter
+ }.With(d =>
+ {
+ d.AddText("mapped by ");
+ d.AddUserLink(beatmap.Metadata.Author);
+ }),
+ }
+ },
+ new PlayCountText(playCount)
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight
+ },
+ }
+ },
+ }
+ }
+ }
+ }
+ };
+ }
+
+ private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer
+ {
+ public MostPlayedBeatmapMetadataContainer(BeatmapInfo beatmap)
+ : base(beatmap)
+ {
+ }
+
+ protected override Drawable[] CreateText(BeatmapInfo beatmap) => new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Text = new LocalisedString((
+ $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ",
+ $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")),
+ Font = OsuFont.GetFont(weight: FontWeight.Bold)
+ },
+ new OsuSpriteText
+ {
+ Text = "by " + new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)),
+ Font = OsuFont.GetFont(weight: FontWeight.Regular)
+ },
+ };
+ }
+
+ private class PlayCountText : CompositeDrawable, IHasTooltip
+ {
+ public string TooltipText => "times played";
+
+ public PlayCountText(int playCount)
+ {
+ AutoSizeAxes = Axes.Both;
+
+ InternalChild = new FillFlowContainer
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ AutoSizeAxes = Axes.Both,
+ Spacing = new Vector2(5, 0),
+ Children = new Drawable[]
+ {
+ new SpriteIcon
+ {
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Size = new Vector2(12),
+ Icon = FontAwesome.Solid.Play,
+ },
+ new OsuSpriteText
+ {
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Text = playCount.ToString(),
+ Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular),
+ },
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Colour = colours.Yellow;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs
deleted file mode 100644
index 1b286f92d3..0000000000
--- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedRow.cs
+++ /dev/null
@@ -1,77 +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.Game.Beatmaps;
-using osu.Game.Beatmaps.Drawables;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Containers;
-using osu.Game.Graphics.Sprites;
-using osuTK;
-
-namespace osu.Game.Overlays.Profile.Sections.Historical
-{
- public class DrawableMostPlayedRow : DrawableProfileRow
- {
- private readonly BeatmapInfo beatmap;
- private readonly int playCount;
-
- public DrawableMostPlayedRow(BeatmapInfo beatmap, int playCount)
- {
- this.beatmap = beatmap;
- this.playCount = playCount;
- }
-
- protected override Drawable CreateLeftVisual() => new UpdateableBeatmapSetCover
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Size = new Vector2(80, 50),
- BeatmapSet = beatmap.BeatmapSet,
- CoverType = BeatmapSetCoverType.List,
- };
-
- [BackgroundDependencyLoader]
- private void load()
- {
- LeftFlowContainer.Add(new BeatmapMetadataContainer(beatmap));
- LeftFlowContainer.Add(new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 12))
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Horizontal,
- }.With(d =>
- {
- d.AddText("mapped by ");
- d.AddUserLink(beatmap.Metadata.Author);
- }));
-
- RightFlowContainer.Add(new FillFlowContainer
- {
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Children = new[]
- {
- new OsuSpriteText
- {
- Anchor = Anchor.BottomRight,
- Origin = Anchor.BottomRight,
- Text = playCount.ToString(),
- Font = OsuFont.GetFont(size: 18, weight: FontWeight.SemiBold, italics: true)
- },
- new OsuSpriteText
- {
- Anchor = Anchor.BottomRight,
- Origin = Anchor.BottomRight,
- Text = @"times played ",
- Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular, italics: true)
- },
- }
- });
- }
- }
-}
diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs
index 6085b0bc05..23072f8d90 100644
--- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs
+++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
protected override void ShowMore()
{
- request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
+ request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage);
request.Success += beatmaps => Schedule(() =>
{
MoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0);
@@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
foreach (var beatmap in beatmaps)
{
- ItemsContainer.Add(new DrawableMostPlayedRow(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount));
+ ItemsContainer.Add(new DrawableMostPlayedBeatmap(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount));
}
});
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
index b77357edd8..e54ce44ca2 100644
--- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
+++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
@@ -10,6 +10,8 @@ using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
+using osu.Game.Beatmaps;
+using osu.Framework.Localisation;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
@@ -51,7 +53,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
RightFlowContainer.Insert(1, text);
- LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap));
+ LeftFlowContainer.Add(new ProfileScoreBeatmapMetadataContainer(Score.Beatmap));
LeftFlowContainer.Add(new DrawableDate(Score.Date));
foreach (Mod mod in Score.Mods)
@@ -64,5 +66,30 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
Width = 60,
FillMode = FillMode.Fit,
};
+
+ private class ProfileScoreBeatmapMetadataContainer : BeatmapMetadataContainer
+ {
+ public ProfileScoreBeatmapMetadataContainer(BeatmapInfo beatmap)
+ : base(beatmap)
+ {
+ }
+
+ protected override Drawable[] CreateText(BeatmapInfo beatmap) => new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Text = new LocalisedString((
+ $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ",
+ $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")),
+ Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold, italics: true)
+ },
+ new OsuSpriteText
+ {
+ Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)),
+ Padding = new MarginPadding { Top = 3 },
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular, italics: true)
+ },
+ };
+ }
}
}
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs
index a149cfa12e..4a9ac6e5c7 100644
--- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs
+++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
protected override void ShowMore()
{
- request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
+ request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage);
request.Success += scores => Schedule(() =>
{
foreach (var s in scores)
diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs
index b72aec7a44..f2a778a874 100644
--- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs
+++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
protected override void ShowMore()
{
- request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
+ request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, ItemsPerPage);
request.Success += activities => Schedule(() =>
{
MoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0);
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
index bf0cd91321..927986159b 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
@@ -71,15 +71,7 @@ namespace osu.Game.Overlays.Toolbar
// Scheduled to allow the flow layout to be computed before the line position is updated
private void moveLineToCurrent() => ScheduleAfterChildren(() =>
{
- foreach (var tabItem in TabContainer)
- {
- if (tabItem.Value.Equals(Current.Value))
- {
- ModeButtonLine.MoveToX(tabItem.DrawPosition.X, !hasInitialPosition ? 0 : 200, Easing.OutQuint);
- break;
- }
- }
-
+ ModeButtonLine.MoveToX(SelectedTab.DrawPosition.X, !hasInitialPosition ? 0 : 200, Easing.OutQuint);
hasInitialPosition = true;
});
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 1f6ca4dd73..e61fac679e 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -7,7 +7,6 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.TypeExtensions;
-using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Audio;
using osu.Game.Graphics;
@@ -179,6 +178,39 @@ namespace osu.Game.Rulesets.Objects.Drawables
UpdateResult(false);
}
+ private double? lifetimeStart;
+
+ public override double LifetimeStart
+ {
+ get => lifetimeStart ?? (HitObject.StartTime - InitialLifetimeOffset);
+ set
+ {
+ base.LifetimeStart = value;
+ lifetimeStart = value;
+ }
+ }
+
+ ///
+ /// A safe offset prior to the start time of at which this may begin displaying contents.
+ /// By default, s are assumed to display their contents within 10 seconds prior to the start time of .
+ ///
+ ///
+ /// This is only used as an optimisation to delay the initial update of this and may be tuned more aggressively if required.
+ /// A more accurate should be set inside for an state.
+ ///
+ protected virtual double InitialLifetimeOffset => 10000;
+
+ ///
+ /// Will be called at least once after this has become not alive.
+ ///
+ public virtual void OnKilled()
+ {
+ foreach (var nested in NestedHitObjects)
+ nested.OnKilled();
+
+ UpdateResult(false);
+ }
+
protected virtual void AddNested(DrawableHitObject h)
{
h.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r);
@@ -223,16 +255,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
OnNewResult?.Invoke(this, Result);
}
- ///
- /// Will called at least once after the of this has been passed.
- ///
- internal void OnLifetimeEnd()
- {
- foreach (var nested in NestedHitObjects)
- nested.OnLifetimeEnd();
- UpdateResult(false);
- }
-
///
/// Processes this , checking if a scoring result has occurred.
///
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
index 6e79d0b766..71e321f205 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
@@ -37,7 +37,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount,
+ List> nodeSamples)
{
newCombo |= forceNewCombo;
comboOffset += extraComboOffset;
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index f5b1cbcebf..d70c1bf7d3 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
else if (type.HasFlag(ConvertHitObjectType.Slider))
{
PathType pathType = PathType.Catmull;
- double length = 0;
+ double? length = null;
string[] pointSplit = split[5].Split('|');
@@ -130,7 +130,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
repeatCount = Math.Max(0, repeatCount - 1);
if (split.Length > 7)
+ {
length = Math.Max(0, Parsing.ParseDouble(split[7]));
+ if (length == 0)
+ length = null;
+ }
if (split.Length > 10)
readCustomSampleBanks(split[10], bankInfo);
@@ -291,7 +295,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// The slider repeat count.
/// The samples to be played when the slider nodes are hit. This includes the head and tail of the slider.
/// The hit object.
- protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples);
+ protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount,
+ List> nodeSamples);
///
/// Creates a legacy Spinner-type hit object.
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
index b20a027e78..94aba95e90 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
@@ -26,7 +26,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount,
+ List> nodeSamples)
{
return new ConvertSlider
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
index 0a4e38df02..65102f1e89 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.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 osuTK;
using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
@@ -38,7 +37,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount,
+ List> nodeSamples)
{
newCombo |= forceNewCombo;
comboOffset += extraComboOffset;
@@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
Position = position,
NewCombo = FirstObject || newCombo,
ComboOffset = comboOffset,
- Path = new SliderPath(pathType, controlPoints, Math.Max(0, length)),
+ Path = new SliderPath(pathType, controlPoints, length),
NodeSamples = nodeSamples,
RepeatCount = repeatCount
};
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
index 7c1514c1eb..eb598f1368 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
@@ -23,7 +23,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
return new ConvertHit();
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List> nodeSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount,
+ List> nodeSamples)
{
return new ConvertSlider
{
diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs
index 2f3a384e95..9485189433 100644
--- a/osu.Game/Rulesets/UI/HitObjectContainer.cs
+++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs
@@ -34,8 +34,14 @@ namespace osu.Game.Rulesets.UI
protected override void OnChildLifetimeBoundaryCrossed(LifetimeBoundaryCrossedEvent e)
{
- if (e.Kind == LifetimeBoundaryKind.End && e.Direction == LifetimeBoundaryCrossingDirection.Forward && e.Child is DrawableHitObject hitObject)
- hitObject.OnLifetimeEnd();
+ if (!(e.Child is DrawableHitObject hitObject))
+ return;
+
+ if ((e.Kind == LifetimeBoundaryKind.End && e.Direction == LifetimeBoundaryCrossingDirection.Forward)
+ || (e.Kind == LifetimeBoundaryKind.Start && e.Direction == LifetimeBoundaryCrossingDirection.Backward))
+ {
+ hitObject.OnKilled();
+ }
}
}
}
diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
index 08f1881038..5225740d0b 100644
--- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
+++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.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;
@@ -168,6 +168,12 @@ namespace osu.Game.Screens.Backgrounds
private void load(OsuConfigManager config)
{
userBlurLevel = config.GetBindable(OsuSetting.BlurLevel);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
userBlurLevel.ValueChanged += _ => UpdateVisuals();
BlurAmount.ValueChanged += _ => UpdateVisuals();
}
diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs
index fff713f026..ae27e53813 100644
--- a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs
+++ b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs
@@ -33,6 +33,8 @@ namespace osu.Game.Screens.Multi.Match.Components
}, true);
}
+ protected override bool IsOnlineScope => true;
+
protected override APIRequest FetchScores(Action> scoresCallback)
{
if (roomId.Value == null)
diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs
index f53fb75e1a..cac26b3dbf 100644
--- a/osu.Game/Screens/Ranking/Results.cs
+++ b/osu.Game/Screens/Ranking/Results.cs
@@ -256,9 +256,12 @@ namespace osu.Game.Screens.Ranking
}
};
- foreach (var t in CreateResultPages())
- modeChangeButtons.AddItem(t);
- modeChangeButtons.Current.Value = modeChangeButtons.Items.FirstOrDefault();
+ var pages = CreateResultPages();
+
+ foreach (var p in pages)
+ modeChangeButtons.AddItem(p);
+
+ modeChangeButtons.Current.Value = pages.FirstOrDefault();
modeChangeButtons.Current.BindValueChanged(page =>
{
diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs
index bd0b0dd5cd..57fe15fd99 100644
--- a/osu.Game/Screens/Select/FilterControl.cs
+++ b/osu.Game/Screens/Select/FilterControl.cs
@@ -120,7 +120,8 @@ namespace osu.Game.Screens.Select
RelativeSizeAxes = Axes.X,
Height = 24,
Width = 0.5f,
- AutoSort = true
+ AutoSort = true,
+ Current = { Value = GroupMode.Title }
},
//spriteText = new OsuSpriteText
//{
@@ -139,6 +140,7 @@ namespace osu.Game.Screens.Select
Width = 0.5f,
Height = 24,
AutoSort = true,
+ Current = { Value = SortMode.Title }
}
}
},
diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs
index c96c5022c0..fce4d1b2e2 100644
--- a/osu.Game/Screens/Select/FooterButtonMods.cs
+++ b/osu.Game/Screens/Select/FooterButtonMods.cs
@@ -9,18 +9,25 @@ using osu.Game.Rulesets.Mods;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osuTK;
using osuTK.Input;
namespace osu.Game.Screens.Select
{
- public class FooterButtonMods : FooterButton
+ public class FooterButtonMods : FooterButton, IHasCurrentValue>
{
- public FooterButtonMods(Bindable> mods)
+ public Bindable> Current
{
- FooterModDisplay modDisplay;
+ get => modDisplay.Current;
+ set => modDisplay.Current = value;
+ }
+ private readonly FooterModDisplay modDisplay;
+
+ public FooterButtonMods()
+ {
Add(new Container
{
Anchor = Anchor.CentreLeft,
@@ -33,9 +40,6 @@ namespace osu.Game.Screens.Select
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Left = 70 }
});
-
- if (mods != null)
- modDisplay.Current = mods;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
index 0f6d4f3188..cb45c00f66 100644
--- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs
@@ -79,6 +79,8 @@ namespace osu.Game.Screens.Select.Leaderboards
};
}
+ protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local;
+
protected override APIRequest FetchScores(Action> scoresCallback)
{
if (Scope == BeatmapLeaderboardScope.Local)
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index b3c3925a26..20dbcf2693 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -221,11 +221,9 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader(true)]
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores)
{
- mods.BindTo(Mods);
-
if (Footer != null)
{
- Footer.AddButton(new FooterButtonMods(mods), ModSelect);
+ Footer.AddButton(new FooterButtonMods { Current = mods }, ModSelect);
Footer.AddButton(new FooterButtonRandom { Action = triggerRandom });
Footer.AddButton(new FooterButtonOptions(), BeatmapOptions);
@@ -253,7 +251,7 @@ namespace osu.Game.Screens.Select
Schedule(() =>
{
// if we have no beatmaps but osu-stable is found, let's prompt the user to import.
- if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable)
+ if (!beatmaps.GetAllUsableBeatmapSetsEnumerable().Any() && beatmaps.StableInstallationAvailable)
dialogOverlay.Push(new ImportFromStablePopup(() =>
{
Task.Run(beatmaps.ImportFromStableAsync).ContinueWith(_ => scores.ImportFromStableAsync(), TaskContinuationOptions.OnlyOnRanToCompletion);
@@ -263,6 +261,13 @@ namespace osu.Game.Screens.Select
}
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ mods.BindTo(Mods);
+ }
+
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs
index 37f4cc28a2..7882e0f31b 100644
--- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs
+++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Skinning
public Drawable GetDrawableComponent(string componentName)
{
Drawable sourceDrawable;
- if (beatmapSkins.Value && (sourceDrawable = skin.GetDrawableComponent(componentName)) != null)
+ if (beatmapSkins.Value && (sourceDrawable = skin?.GetDrawableComponent(componentName)) != null)
return sourceDrawable;
return fallbackSource?.GetDrawableComponent(componentName);
@@ -43,7 +43,7 @@ namespace osu.Game.Skinning
public Texture GetTexture(string componentName)
{
Texture sourceTexture;
- if (beatmapSkins.Value && (sourceTexture = skin.GetTexture(componentName)) != null)
+ if (beatmapSkins.Value && (sourceTexture = skin?.GetTexture(componentName)) != null)
return sourceTexture;
return fallbackSource.GetTexture(componentName);
@@ -52,7 +52,7 @@ namespace osu.Game.Skinning
public SampleChannel GetSample(string sampleName)
{
SampleChannel sourceChannel;
- if (beatmapHitsounds.Value && (sourceChannel = skin.GetSample(sampleName)) != null)
+ if (beatmapHitsounds.Value && (sourceChannel = skin?.GetSample(sampleName)) != null)
return sourceChannel;
return fallbackSource?.GetSample(sampleName);
diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs
index a49f2d079b..795b90ba11 100644
--- a/osu.Game/Users/Drawables/UpdateableAvatar.cs
+++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs
@@ -5,7 +5,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
-using osu.Framework.Graphics.Transforms;
namespace osu.Game.Users.Drawables
{
@@ -38,8 +37,6 @@ namespace osu.Game.Users.Drawables
set => base.EdgeEffect = value;
}
- protected override bool TransformImmediately { get; }
-
///
/// Whether to show a default guest representation on null user (as opposed to nothing).
///
@@ -50,14 +47,11 @@ namespace osu.Game.Users.Drawables
///
public readonly BindableBool OpenOnClick = new BindableBool(true);
- public UpdateableAvatar(User user = null, bool hideImmediately = false)
+ public UpdateableAvatar(User user = null)
{
- TransformImmediately = hideImmediately;
User = user;
}
- protected override TransformSequence ApplyHideTransforms(Drawable drawable) => TransformImmediately ? drawable?.FadeOut() : base.ApplyHideTransforms(drawable);
-
protected override Drawable CreateDrawable(User user)
{
if (user == null && !ShowGuestOnNull)
diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs
index 78d1a8de20..abc16b2390 100644
--- a/osu.Game/Users/Drawables/UpdateableFlag.cs
+++ b/osu.Game/Users/Drawables/UpdateableFlag.cs
@@ -3,7 +3,6 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Transforms;
namespace osu.Game.Users.Drawables
{
@@ -15,21 +14,16 @@ namespace osu.Game.Users.Drawables
set => Model = value;
}
- protected override bool TransformImmediately { get; }
-
///
/// Whether to show a place holder on null country.
///
public bool ShowPlaceholderOnNull = true;
- public UpdateableFlag(Country country = null, bool hideImmediately = false)
+ public UpdateableFlag(Country country = null)
{
- TransformImmediately = hideImmediately;
Country = country;
}
- protected override TransformSequence ApplyHideTransforms(Drawable drawable) => TransformImmediately ? drawable?.FadeOut() : base.ApplyHideTransforms(drawable);
-
protected override Drawable CreateDrawable(Country country)
{
if (country == null && !ShowPlaceholderOnNull)
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 436ba90a88..d90b1d36e1 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index c24349bcb5..fa2521a19e 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -105,8 +105,8 @@
-
-
+
+