diff --git a/osu.Android.props b/osu.Android.props
index 8237a570ff..5f3fb858ee 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,8 +51,8 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
index 94655f3cf7..c7e3516d62 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -248,6 +248,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
break;
}
+ slider.Path.ExpectedDistance.Value = null;
piece.ControlPoint.Type = type;
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs
index bea5d4f5d9..0a1aab9ef1 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs
@@ -56,6 +56,8 @@ namespace osu.Game.Rulesets.Osu.Mods
{
switch (nested)
{
+ //Freezing the SliderTicks doesnt play well with snaking sliders
+ case SliderTick:
//SliderRepeat wont layer correctly if preempt is changed.
case SliderRepeat:
break;
diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs
index 46c8e7c02a..c19ed3fb35 100644
--- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs
@@ -49,7 +49,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
private const float max_rotation = 0.25f;
public IShader? TextureShader { get; private set; }
- public IShader? RoundedTextureShader { get; private set; }
protected Texture? Texture { get; set; }
@@ -69,7 +68,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
[BackgroundDependencyLoader]
private void load(ShaderManager shaders)
{
- RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
}
@@ -247,18 +245,16 @@ namespace osu.Game.Rulesets.Osu.Skinning
texture ??= renderer.WhitePixel;
RectangleF textureRect = texture.GetTextureRect();
- var shader = GetAppropriateShader(renderer);
-
renderer.SetBlend(BlendingParameters.Additive);
renderer.PushLocalMatrix(DrawInfo.Matrix);
- shader.Bind();
+ TextureShader.Bind();
texture.Bind();
for (int i = 0; i < points.Count; i++)
drawPointQuad(points[i], textureRect, i + firstVisiblePointIndex);
- shader.Unbind();
+ TextureShader.Unbind();
renderer.PopLocalMatrix();
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableSwell.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableSwell.cs
new file mode 100644
index 0000000000..b8c0f6f11e
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableSwell.cs
@@ -0,0 +1,39 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Taiko.Objects;
+using osu.Game.Rulesets.Taiko.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Taiko.Tests.Skinning
+{
+ [TestFixture]
+ public class TestSceneDrawableSwell : TaikoSkinnableTestScene
+ {
+ [Test]
+ public void TestHits()
+ {
+ AddStep("Centre hit", () => SetContents(_ => new DrawableSwell(createHitAtCurrentTime())
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }));
+ }
+
+ private Swell createHitAtCurrentTime()
+ {
+ var hit = new Swell
+ {
+ StartTime = Time.Current + 3000,
+ EndTime = Time.Current + 6000,
+ };
+
+ hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
+ return hit;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs
index 22f96da61e..c22c0e9e79 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Taiko.Objects;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Taiko.Skinning.Argon
@@ -81,12 +82,15 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
updateStateTransforms(drawableHitObject, drawableHitObject.State.Value);
}
- private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
+ private void updateStateTransforms(DrawableHitObject h, ArmedState state)
{
+ if (h.HitObject is not Hit)
+ return;
+
switch (state)
{
case ArmedState.Hit:
- using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime))
+ using (BeginAbsoluteSequence(h.HitStateUpdateTime))
{
flash.FadeTo(0.9f).FadeOut(500, Easing.OutQuint);
}
diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonSwellCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonSwellCirclePiece.cs
new file mode 100644
index 0000000000..82a6e34128
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonSwellCirclePiece.cs
@@ -0,0 +1,34 @@
+// 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.Colour;
+using osu.Framework.Graphics.Sprites;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Taiko.Skinning.Argon
+{
+ public class ArgonSwellCirclePiece : ArgonCirclePiece
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ AccentColour = ColourInfo.GradientVertical(
+ new Color4(240, 201, 0, 255),
+ new Color4(167, 139, 0, 255)
+ );
+
+ AddInternal(new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Icon = FontAwesome.Solid.Asterisk,
+ Size = new Vector2(ICON_SIZE),
+ Scale = new Vector2(0.8f, 1)
+ });
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/TaikoArgonSkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/TaikoArgonSkinTransformer.cs
index 6d1087793d..a5d091a1c8 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/Argon/TaikoArgonSkinTransformer.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/TaikoArgonSkinTransformer.cs
@@ -60,6 +60,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
case TaikoSkinComponents.TaikoExplosionMiss:
case TaikoSkinComponents.TaikoExplosionOk:
return new ArgonHitExplosion(taikoComponent.Component);
+
+ case TaikoSkinComponents.Swell:
+ return new ArgonSwellCirclePiece();
}
break;
diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs
index ccde8e6ac9..7ddc413d98 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs
@@ -153,12 +153,15 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default
updateStateTransforms(drawableHitObject, drawableHitObject.State.Value);
}
- private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
+ private void updateStateTransforms(DrawableHitObject h, ArmedState state)
{
+ if (h.HitObject is not Hit)
+ return;
+
switch (state)
{
case ArmedState.Hit:
- using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime))
+ using (BeginAbsoluteSequence(h.HitStateUpdateTime))
flashBox.FadeTo(0.9f).FadeOut(300);
break;
}
diff --git a/osu.Game.Tests/Chat/TestSceneChannelManager.cs b/osu.Game.Tests/Chat/TestSceneChannelManager.cs
index 32fc2604ba..c9a78cbf59 100644
--- a/osu.Game.Tests/Chat/TestSceneChannelManager.cs
+++ b/osu.Game.Tests/Chat/TestSceneChannelManager.cs
@@ -23,6 +23,7 @@ namespace osu.Game.Tests.Chat
private ChannelManager channelManager;
private int currentMessageId;
private List sentMessages;
+ private List silencedUserIds;
[SetUp]
public void Setup() => Schedule(() =>
@@ -39,6 +40,7 @@ namespace osu.Game.Tests.Chat
{
currentMessageId = 0;
sentMessages = new List();
+ silencedUserIds = new List();
((DummyAPIAccess)API).HandleRequest = req =>
{
@@ -56,6 +58,11 @@ namespace osu.Game.Tests.Chat
handleMarkChannelAsReadRequest(markRead);
return true;
+ case ChatAckRequest ack:
+ ack.TriggerSuccess(new ChatAckResponse { Silences = silencedUserIds.Select(u => new ChatSilence { UserId = u }).ToList() });
+ silencedUserIds.Clear();
+ return true;
+
case GetUpdatesRequest updatesRequest:
updatesRequest.TriggerSuccess(new GetUpdatesResponse
{
@@ -115,6 +122,28 @@ namespace osu.Game.Tests.Chat
AddAssert("channel's last read ID is set to the latest message", () => channel.LastReadId == sentMessages.Last().Id);
}
+ [Test]
+ public void TestSilencedUsersAreRemoved()
+ {
+ Channel channel = null;
+
+ AddStep("join channel and select it", () =>
+ {
+ channelManager.JoinChannel(channel = createChannel(1, ChannelType.Public));
+ channelManager.CurrentChannel.Value = channel;
+ });
+
+ AddStep("post message", () => channelManager.PostMessage("Definitely something bad"));
+
+ AddStep("mark user as silenced and send ack request", () =>
+ {
+ silencedUserIds.Add(API.LocalUser.Value.OnlineID);
+ channelManager.SendAck();
+ });
+
+ AddAssert("channel has no more messages", () => channel.Messages, () => Is.Empty);
+ }
+
private void handlePostMessageRequest(PostMessageRequest request)
{
var message = new Message(++currentMessageId)
diff --git a/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs b/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs
new file mode 100644
index 0000000000..81a3249efb
--- /dev/null
+++ b/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs
@@ -0,0 +1,40 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Graphics.Backgrounds;
+using osu.Framework.Graphics;
+using osuTK.Graphics;
+using osu.Framework.Graphics.Shapes;
+
+namespace osu.Game.Tests.Visual.Background
+{
+ public class TestSceneTrianglesBackground : OsuTestScene
+ {
+ private readonly Triangles triangles;
+
+ public TestSceneTrianglesBackground()
+ {
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black
+ },
+ triangles = new Triangles
+ {
+ RelativeSizeAxes = Axes.Both,
+ ColourLight = Color4.White,
+ ColourDark = Color4.Gray
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ AddSliderStep("Triangle scale", 0f, 10f, 1f, s => triangles.TriangleScale = s);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs
index 7c668adba5..b90b9b437d 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs
@@ -163,10 +163,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for bars to disappear", () => !this.ChildrenOfType().Any());
AddUntilStep("ensure max circles not exceeded", () =>
- {
- return this.ChildrenOfType()
- .All(m => m.ChildrenOfType().Count() <= max_displayed_judgements);
- });
+ this.ChildrenOfType().First().ChildrenOfType().Count(), () => Is.LessThanOrEqualTo(max_displayed_judgements));
AddStep("show displays", () =>
{
diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
index d58887c090..e500efede3 100644
--- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
@@ -436,6 +436,8 @@ namespace osu.Game.Tests.Visual.Navigation
{
AddUntilStep("Wait for toolbar to load", () => Game.Toolbar.IsLoaded);
+ AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
+
TestPlaySongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
index 0b982a5745..0b75a2aa05 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -40,8 +40,10 @@ namespace osu.Game.Tests.Visual.Online
private ChannelManager channelManager;
private readonly APIUser testUser = new APIUser { Username = "test user", Id = 5071479 };
+ private readonly APIUser testUser1 = new APIUser { Username = "test user", Id = 5071480 };
private Channel[] testChannels;
+ private Message[] initialMessages;
private Channel testChannel1 => testChannels[0];
private Channel testChannel2 => testChannels[1];
@@ -49,10 +51,14 @@ namespace osu.Game.Tests.Visual.Online
[Resolved]
private OsuConfigManager config { get; set; } = null!;
+ private int currentMessageId;
+
[SetUp]
public void SetUp() => Schedule(() =>
{
+ currentMessageId = 0;
testChannels = Enumerable.Range(1, 10).Select(createPublicChannel).ToArray();
+ initialMessages = testChannels.SelectMany(createChannelMessages).ToArray();
Child = new DependencyProvidingContainer
{
@@ -99,7 +105,7 @@ namespace osu.Game.Tests.Visual.Online
return true;
case GetMessagesRequest getMessages:
- getMessages.TriggerSuccess(createChannelMessages(getMessages.Channel));
+ getMessages.TriggerSuccess(initialMessages.ToList());
return true;
case GetUserRequest getUser:
@@ -495,6 +501,35 @@ namespace osu.Game.Tests.Visual.Online
waitForChannel1Visible();
}
+ [Test]
+ public void TestRemoveMessages()
+ {
+ AddStep("Show overlay with channel", () =>
+ {
+ chatOverlay.Show();
+ channelManager.CurrentChannel.Value = channelManager.JoinChannel(testChannel1);
+ });
+
+ AddAssert("Overlay is visible", () => chatOverlay.State.Value == Visibility.Visible);
+ waitForChannel1Visible();
+
+ AddStep("Send message from another user", () =>
+ {
+ testChannel1.AddNewMessages(new Message
+ {
+ ChannelId = testChannel1.Id,
+ Content = "Message from another user",
+ Timestamp = DateTimeOffset.Now,
+ Sender = testUser1,
+ });
+ });
+
+ AddStep("Remove messages from other user", () =>
+ {
+ testChannel1.RemoveMessagesFromUser(testUser.Id);
+ });
+ }
+
private void joinTestChannel(int i)
{
AddStep($"Join test channel {i}", () => channelManager.JoinChannel(testChannels[i]));
@@ -546,7 +581,7 @@ namespace osu.Game.Tests.Visual.Online
private List createChannelMessages(Channel channel)
{
- var message = new Message
+ var message = new Message(currentMessageId++)
{
ChannelId = channel.Id,
Content = $"Hello, this is a message in {channel.Name}",
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index 63532fdba8..b6b9e8926b 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -1055,6 +1055,18 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("mod overlay hidden", () => songSelect!.ModSelect.State.Value == Visibility.Hidden);
}
+ [Test]
+ public void TestBeatmapOptionsDisabled()
+ {
+ createSongSelect();
+
+ addRulesetImportStep(0);
+
+ AddAssert("options enabled", () => songSelect.ChildrenOfType().Single().Enabled.Value);
+ AddStep("delete all beatmaps", () => manager.Delete());
+ AddAssert("options disabled", () => !songSelect.ChildrenOfType().Single().Enabled.Value);
+ }
+
private void waitForInitialSelection()
{
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs
index cb78fbfe35..0a88abface 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs
@@ -3,8 +3,10 @@
#nullable disable
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
+using osu.Framework.Testing;
using osu.Game.Screens.Select;
using osuTK;
using osuTK.Input;
@@ -43,6 +45,12 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.MoveMouseTo(Vector2.Zero);
});
+ [Test]
+ public void TestState()
+ {
+ AddRepeatStep("toggle options state", () => this.ChildrenOfType().Last().Enabled.Toggle(), 20);
+ }
+
[Test]
public void TestFooterRandom()
{
diff --git a/osu.Game/Beatmaps/Drawables/Cards/ExpandedContentScrollContainer.cs b/osu.Game/Beatmaps/Drawables/Cards/ExpandedContentScrollContainer.cs
index a80a7998a5..c0ed6ac1a9 100644
--- a/osu.Game/Beatmaps/Drawables/Cards/ExpandedContentScrollContainer.cs
+++ b/osu.Game/Beatmaps/Drawables/Cards/ExpandedContentScrollContainer.cs
@@ -59,6 +59,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards
return base.OnScroll(e);
}
+ protected override bool OnClick(ClickEvent e) => true;
+
private class ExpandedContentScrollbar : OsuScrollbar
{
public ExpandedContentScrollbar(Direction scrollDir)
diff --git a/osu.Game/Configuration/IGameplaySettings.cs b/osu.Game/Configuration/IGameplaySettings.cs
index a35bdd20d0..8d66535017 100644
--- a/osu.Game/Configuration/IGameplaySettings.cs
+++ b/osu.Game/Configuration/IGameplaySettings.cs
@@ -8,7 +8,7 @@ namespace osu.Game.Configuration
{
///
/// A settings provider which generally sources from (global user settings)
- /// but can allow overriding settings by caching more locally. For instance, in the editor.
+ /// but can allow overriding settings by caching more locally. For instance, in the editor compose screen.
///
///
/// More settings can be moved into this interface as required.
diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs
index 1166a86814..09d137011c 100644
--- a/osu.Game/Graphics/Backgrounds/Triangles.cs
+++ b/osu.Game/Graphics/Backgrounds/Triangles.cs
@@ -17,6 +17,7 @@ using System.Collections.Generic;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Rendering.Vertices;
using osu.Framework.Lists;
+using osu.Framework.Bindables;
namespace osu.Game.Graphics.Backgrounds
{
@@ -25,6 +26,11 @@ namespace osu.Game.Graphics.Backgrounds
private const float triangle_size = 100;
private const float base_velocity = 50;
+ ///
+ /// sqrt(3) / 2
+ ///
+ private const float equilateral_triangle_ratio = 0.866f;
+
///
/// How many screen-space pixels are smoothed over.
/// Same behavior as Sprite's EdgeSmoothness.
@@ -69,7 +75,13 @@ namespace osu.Game.Graphics.Backgrounds
///
protected virtual float SpawnRatio => 1;
- private float triangleScale = 1;
+ private readonly BindableFloat triangleScale = new BindableFloat(1f);
+
+ public float TriangleScale
+ {
+ get => triangleScale.Value;
+ set => triangleScale.Value = value;
+ }
///
/// Whether we should drop-off alpha values of triangles more quickly to improve
@@ -103,30 +115,13 @@ namespace osu.Game.Graphics.Backgrounds
private void load(IRenderer renderer, ShaderManager shaders)
{
texture = renderer.WhitePixel;
- shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
+ shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
}
protected override void LoadComplete()
{
base.LoadComplete();
- addTriangles(true);
- }
-
- public float TriangleScale
- {
- get => triangleScale;
- set
- {
- float change = value / triangleScale;
- triangleScale = value;
-
- for (int i = 0; i < parts.Count; i++)
- {
- TriangleParticle newParticle = parts[i];
- newParticle.Scale *= change;
- parts[i] = newParticle;
- }
- }
+ triangleScale.BindValueChanged(_ => Reset(), true);
}
protected override void Update()
@@ -147,7 +142,7 @@ namespace osu.Game.Graphics.Backgrounds
// Since position is relative, the velocity needs to scale inversely with DrawHeight.
// Since we will later multiply by the scale of individual triangles we normalize by
// dividing by triangleScale.
- float movedDistance = -elapsedSeconds * Velocity * base_velocity / (DrawHeight * triangleScale);
+ float movedDistance = -elapsedSeconds * Velocity * base_velocity / (DrawHeight * TriangleScale);
for (int i = 0; i < parts.Count; i++)
{
@@ -159,7 +154,7 @@ namespace osu.Game.Graphics.Backgrounds
parts[i] = newParticle;
- float bottomPos = parts[i].Position.Y + triangle_size * parts[i].Scale * 0.866f / DrawHeight;
+ float bottomPos = parts[i].Position.Y + triangle_size * parts[i].Scale * equilateral_triangle_ratio / DrawHeight;
if (bottomPos < 0)
parts.RemoveAt(i);
}
@@ -185,9 +180,11 @@ namespace osu.Game.Graphics.Backgrounds
// Limited by the maximum size of QuadVertexBuffer for safety.
const int max_triangles = ushort.MaxValue / (IRenderer.VERTICES_PER_QUAD + 2);
- AimCount = (int)Math.Min(max_triangles, (DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio));
+ AimCount = (int)Math.Min(max_triangles, DrawWidth * DrawHeight * 0.002f / (TriangleScale * TriangleScale) * SpawnRatio);
- for (int i = 0; i < AimCount - parts.Count; i++)
+ int currentCount = parts.Count;
+
+ for (int i = 0; i < AimCount - currentCount; i++)
parts.Add(createTriangle(randomY));
}
@@ -195,13 +192,27 @@ namespace osu.Game.Graphics.Backgrounds
{
TriangleParticle particle = CreateTriangle();
- particle.Position = new Vector2(nextRandom(), randomY ? nextRandom() : 1);
+ particle.Position = getRandomPosition(randomY, particle.Scale);
particle.ColourShade = nextRandom();
particle.Colour = CreateTriangleShade(particle.ColourShade);
return particle;
}
+ private Vector2 getRandomPosition(bool randomY, float scale)
+ {
+ float y = 1;
+
+ if (randomY)
+ {
+ // since triangles are drawn from the top - allow them to be positioned a bit above the screen
+ float maxOffset = triangle_size * scale * equilateral_triangle_ratio / DrawHeight;
+ y = Interpolation.ValueAt(nextRandom(), -maxOffset, 1f, 0f, 1f);
+ }
+
+ return new Vector2(nextRandom(), y);
+ }
+
///
/// Creates a triangle particle with a random scale.
///
@@ -214,7 +225,7 @@ namespace osu.Game.Graphics.Backgrounds
float u1 = 1 - nextRandom(); //uniform(0,1] random floats
float u2 = 1 - nextRandom();
float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); // random normal(0,1)
- float scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); // random normal(mean,stdDev^2)
+ float scale = Math.Max(TriangleScale * (mean + std_dev * randStdNormal), 0.1f); // random normal(mean,stdDev^2)
return new TriangleParticle { Scale = scale };
}
@@ -284,7 +295,7 @@ namespace osu.Game.Graphics.Backgrounds
foreach (TriangleParticle particle in parts)
{
- var offset = triangle_size * new Vector2(particle.Scale * 0.5f, particle.Scale * 0.866f);
+ var offset = triangle_size * new Vector2(particle.Scale * 0.5f, particle.Scale * equilateral_triangle_ratio);
var triangle = new Triangle(
Vector2Extensions.Transform(particle.Position * size, DrawInfo.Matrix),
diff --git a/osu.Game/Graphics/Sprites/LogoAnimation.cs b/osu.Game/Graphics/Sprites/LogoAnimation.cs
index 7debdc7a37..097de4dfcb 100644
--- a/osu.Game/Graphics/Sprites/LogoAnimation.cs
+++ b/osu.Game/Graphics/Sprites/LogoAnimation.cs
@@ -17,7 +17,6 @@ namespace osu.Game.Graphics.Sprites
private void load(ShaderManager shaders)
{
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"LogoAnimation");
- RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"LogoAnimation"); // Masking isn't supported for now
}
private float animationProgress;
@@ -58,7 +57,7 @@ namespace osu.Game.Graphics.Sprites
protected override void Blit(IRenderer renderer)
{
- GetAppropriateShader(renderer).GetUniform("progress").UpdateValue(ref progress);
+ TextureShader.GetUniform("progress").UpdateValue(ref progress);
base.Blit(renderer);
}
diff --git a/osu.Game/Online/API/Requests/ChatAckRequest.cs b/osu.Game/Online/API/Requests/ChatAckRequest.cs
index f09df4908e..306b5acc1d 100644
--- a/osu.Game/Online/API/Requests/ChatAckRequest.cs
+++ b/osu.Game/Online/API/Requests/ChatAckRequest.cs
@@ -7,12 +7,30 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
+ ///
+ /// A request which should be sent occasionally while interested in chat and online state.
+ ///
+ /// This will:
+ /// - Mark the user as "online" (for 10 minutes since the last invocation).
+ /// - Return any silences since the last invocation (if either or is not null).
+ ///
+ /// For silence handling, a should be provided as soon as a message is received by the client.
+ /// From that point forward, should be preferred after the first
+ /// arrives in a response from the ack request. Specifying both parameters will prioritise the latter.
+ ///
public class ChatAckRequest : APIRequest
{
+ public long? SinceMessageId;
+ public uint? SinceSilenceId;
+
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.Method = HttpMethod.Post;
+ if (SinceMessageId != null)
+ req.AddParameter(@"since", SinceMessageId.ToString());
+ if (SinceSilenceId != null)
+ req.AddParameter(@"history_since", SinceSilenceId.Value.ToString());
return req;
}
diff --git a/osu.Game/Online/API/Requests/Responses/ChatSilence.cs b/osu.Game/Online/API/Requests/Responses/ChatSilence.cs
index 45fd6e1ba3..afb44e385e 100644
--- a/osu.Game/Online/API/Requests/Responses/ChatSilence.cs
+++ b/osu.Game/Online/API/Requests/Responses/ChatSilence.cs
@@ -12,6 +12,6 @@ namespace osu.Game.Online.API.Requests.Responses
public uint Id { get; set; }
[JsonProperty("user_id")]
- public uint UserId { get; set; }
+ public int UserId { get; set; }
}
}
diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs
index ada9e22027..24b384b1d4 100644
--- a/osu.Game/Online/Chat/Channel.cs
+++ b/osu.Game/Online/Chat/Channel.cs
@@ -157,6 +157,20 @@ namespace osu.Game.Online.Chat
NewMessagesArrived?.Invoke(messages);
}
+ public void RemoveMessagesFromUser(int userId)
+ {
+ for (int i = 0; i < Messages.Count; i++)
+ {
+ var message = Messages[i];
+
+ if (message.SenderId == userId)
+ {
+ Messages.RemoveAt(i--);
+ MessageRemoved?.Invoke(message);
+ }
+ }
+ }
+
///
/// Replace or remove a message from the channel.
///
diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index 076f79a700..25a53360f0 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -74,6 +74,9 @@ namespace osu.Game.Online.Chat
private bool channelsInitialised;
private ScheduledDelegate scheduledAck;
+ private long? lastSilenceMessageId;
+ private uint? lastSilenceId;
+
public ChannelManager(IAPIProvider api)
{
this.api = api;
@@ -105,28 +108,7 @@ namespace osu.Game.Online.Chat
connector.Start();
apiState.BindTo(api.State);
- apiState.BindValueChanged(_ => performChatAckRequest(), true);
- }
-
- private void performChatAckRequest()
- {
- if (apiState.Value != APIState.Online)
- return;
-
- scheduledAck?.Cancel();
-
- var req = new ChatAckRequest();
- req.Success += _ => scheduleNextRequest();
- req.Failure += _ => scheduleNextRequest();
- api.Queue(req);
-
- // Todo: Handle silences.
-
- void scheduleNextRequest()
- {
- scheduledAck?.Cancel();
- scheduledAck = Scheduler.AddDelayed(performChatAckRequest, 60000);
- }
+ apiState.BindValueChanged(_ => SendAck(), true);
}
///
@@ -349,6 +331,8 @@ namespace osu.Game.Online.Chat
foreach (var group in messages.GroupBy(m => m.ChannelId))
channels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
+
+ lastSilenceMessageId ??= messages.LastOrDefault()?.Id;
}
private void initializeChannels()
@@ -398,6 +382,44 @@ namespace osu.Game.Online.Chat
api.Queue(fetchInitialMsgReq);
}
+ ///
+ /// Sends an acknowledgement request to the API.
+ /// This marks the user as online to receive messages from public channels, while also returning a list of silenced users.
+ /// It needs to be called at least once every 10 minutes to remain visibly marked as online.
+ ///
+ public void SendAck()
+ {
+ if (apiState.Value != APIState.Online)
+ return;
+
+ var req = new ChatAckRequest
+ {
+ SinceMessageId = lastSilenceMessageId,
+ SinceSilenceId = lastSilenceId
+ };
+
+ req.Failure += _ => scheduleNextRequest();
+ req.Success += ack =>
+ {
+ foreach (var silence in ack.Silences)
+ {
+ foreach (var channel in JoinedChannels)
+ channel.RemoveMessagesFromUser(silence.UserId);
+ lastSilenceId = Math.Max(lastSilenceId ?? 0, silence.Id);
+ }
+
+ scheduleNextRequest();
+ };
+
+ api.Queue(req);
+
+ void scheduleNextRequest()
+ {
+ scheduledAck?.Cancel();
+ scheduledAck = Scheduler.AddDelayed(SendAck, 60000);
+ }
+ }
+
///
/// Find an existing channel instance for the provided channel. Lookup is performed basd on ID.
/// The provided channel may be used if an existing instance is not found.
diff --git a/osu.Game/Online/Notifications/WebSocket/WebSocketNotificationsClient.cs b/osu.Game/Online/Notifications/WebSocket/WebSocketNotificationsClient.cs
index 86836099d8..d8d78297e3 100644
--- a/osu.Game/Online/Notifications/WebSocket/WebSocketNotificationsClient.cs
+++ b/osu.Game/Online/Notifications/WebSocket/WebSocketNotificationsClient.cs
@@ -72,7 +72,6 @@ namespace osu.Game.Online.Notifications.WebSocket
break;
}
- Logger.Log($"{GetType().ReadableName()} handling event: {message.Event}");
await onMessageReceivedAsync(message);
}
diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs
index c278c9cb93..2d9583b864 100644
--- a/osu.Game/Overlays/BeatmapListingOverlay.cs
+++ b/osu.Game/Overlays/BeatmapListingOverlay.cs
@@ -41,11 +41,8 @@ namespace osu.Game.Overlays
private IBindable apiUser;
- private Drawable currentContent;
private Container panelTarget;
private FillFlowContainer foundContent;
- private NotFoundDrawable notFoundContent;
- private SupporterRequiredDrawable supporterRequiredContent;
private BeatmapListingFilterControl filterControl;
public BeatmapListingOverlay()
@@ -86,11 +83,6 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.X,
Masking = true,
Padding = new MarginPadding { Horizontal = 20 },
- Children = new Drawable[]
- {
- notFoundContent = new NotFoundDrawable(),
- supporterRequiredContent = new SupporterRequiredDrawable(),
- }
}
},
},
@@ -107,7 +99,7 @@ namespace osu.Game.Overlays
apiUser.BindValueChanged(_ => Schedule(() =>
{
if (api.IsLoggedIn)
- addContentToResultsArea(Drawable.Empty());
+ replaceResultsAreaContent(Drawable.Empty());
}));
}
@@ -155,8 +147,8 @@ namespace osu.Game.Overlays
if (searchResult.Type == BeatmapListingFilterControl.SearchResultType.SupporterOnlyFilters)
{
- supporterRequiredContent.UpdateText(searchResult.SupporterOnlyFiltersUsed);
- addContentToResultsArea(supporterRequiredContent);
+ var supporterOnly = new SupporterRequiredDrawable(searchResult.SupporterOnlyFiltersUsed);
+ replaceResultsAreaContent(supporterOnly);
return;
}
@@ -167,13 +159,13 @@ namespace osu.Game.Overlays
//No matches case
if (!newCards.Any())
{
- addContentToResultsArea(notFoundContent);
+ replaceResultsAreaContent(new NotFoundDrawable());
return;
}
var content = createCardContainerFor(newCards);
- panelLoadTask = LoadComponentAsync(foundContent = content, addContentToResultsArea, (cancellationToken = new CancellationTokenSource()).Token);
+ panelLoadTask = LoadComponentAsync(foundContent = content, replaceResultsAreaContent, (cancellationToken = new CancellationTokenSource()).Token);
}
else
{
@@ -221,36 +213,16 @@ namespace osu.Game.Overlays
return content;
}
- private void addContentToResultsArea(Drawable content)
+ private void replaceResultsAreaContent(Drawable content)
{
Loading.Hide();
lastFetchDisplayedTime = Time.Current;
- if (content == currentContent)
- return;
-
- var lastContent = currentContent;
-
- if (lastContent != null)
- {
- lastContent.FadeOut();
- if (!isPlaceholderContent(lastContent))
- lastContent.Expire();
- }
-
- if (!content.IsAlive)
- panelTarget.Add(content);
+ panelTarget.Child = content;
content.FadeInFromZero();
- currentContent = content;
}
- ///
- /// Whether is a static placeholder reused multiple times by this overlay.
- ///
- private bool isPlaceholderContent(Drawable drawable)
- => drawable == notFoundContent || drawable == supporterRequiredContent;
-
private void onCardSizeChanged()
{
if (foundContent?.IsAlive != true || !foundContent.Any())
@@ -287,7 +259,7 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
- private void load(TextureStore textures)
+ private void load(LargeTextureStore textures)
{
AddInternal(new FillFlowContainer
{
@@ -324,15 +296,19 @@ namespace osu.Game.Overlays
{
private LinkFlowContainer supporterRequiredText;
- public SupporterRequiredDrawable()
+ private readonly List filtersUsed;
+
+ public SupporterRequiredDrawable(List filtersUsed)
{
RelativeSizeAxes = Axes.X;
Height = 225;
Alpha = 0;
+
+ this.filtersUsed = filtersUsed;
}
[BackgroundDependencyLoader]
- private void load(TextureStore textures)
+ private void load(LargeTextureStore textures)
{
AddInternal(new FillFlowContainer
{
@@ -360,14 +336,9 @@ namespace osu.Game.Overlays
},
}
});
- }
-
- public void UpdateText(List filters)
- {
- supporterRequiredText.Clear();
supporterRequiredText.AddText(
- BeatmapsStrings.ListingSearchSupporterFilterQuoteDefault(string.Join(" and ", filters), "").ToString(),
+ BeatmapsStrings.ListingSearchSupporterFilterQuoteDefault(string.Join(" and ", filtersUsed), "").ToString(),
t =>
{
t.Font = OsuFont.GetFont(size: 16);
diff --git a/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs b/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs
index 8f5c942b6e..1d904526fd 100644
--- a/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs
+++ b/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs
@@ -119,22 +119,17 @@ namespace osu.Game.Overlays.Dashboard.Home.News
[BackgroundDependencyLoader]
private void load(GameHost host)
{
- NewsPostBackground bg;
-
- Child = new DelayedLoadWrapper(bg = new NewsPostBackground(post.FirstImage)
+ Child = new DelayedLoadUnloadWrapper(() => new NewsPostBackground(post.FirstImage)
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Alpha = 0
})
{
RelativeSizeAxes = Axes.Both
};
- bg.OnLoadComplete += d => d.FadeIn(250, Easing.In);
-
TooltipText = "view in browser";
Action = () => host.OpenUrlExternally("https://osu.ppy.sh/home/news/" + post.Slug);
diff --git a/osu.Game/Overlays/News/NewsCard.cs b/osu.Game/Overlays/News/NewsCard.cs
index eb76522e11..c8e0b0c7ef 100644
--- a/osu.Game/Overlays/News/NewsCard.cs
+++ b/osu.Game/Overlays/News/NewsCard.cs
@@ -49,7 +49,6 @@ namespace osu.Game.Overlays.News
Action = () => host.OpenUrlExternally("https://osu.ppy.sh/home/news/" + post.Slug);
}
- NewsPostBackground bg;
AddRange(new Drawable[]
{
background = new Box
@@ -71,14 +70,14 @@ namespace osu.Game.Overlays.News
CornerRadius = 6,
Children = new Drawable[]
{
- new DelayedLoadWrapper(bg = new NewsPostBackground(post.FirstImage)
+ new DelayedLoadUnloadWrapper(() => new NewsPostBackground(post.FirstImage)
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0
- })
+ }, timeBeforeUnload: 5000)
{
RelativeSizeAxes = Axes.Both
},
@@ -116,8 +115,6 @@ namespace osu.Game.Overlays.News
IdleColour = colourProvider.Background4;
HoverColour = colourProvider.Background3;
- bg.OnLoadComplete += d => d.FadeIn(250, Easing.In);
-
main.AddParagraph(post.Title, t => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold));
main.AddParagraph(post.Preview, t => t.Font = OsuFont.GetFont(size: 12)); // Should use sans-serif font
main.AddParagraph("by ", t => t.Font = OsuFont.GetFont(size: 12));
diff --git a/osu.Game/Overlays/News/NewsPostBackground.cs b/osu.Game/Overlays/News/NewsPostBackground.cs
index bddca8f7ec..b77623842c 100644
--- a/osu.Game/Overlays/News/NewsPostBackground.cs
+++ b/osu.Game/Overlays/News/NewsPostBackground.cs
@@ -4,6 +4,7 @@
#nullable disable
using osu.Framework.Allocation;
+using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
@@ -25,6 +26,12 @@ namespace osu.Game.Overlays.News
Texture = store.Get(createUrl(sourceUrl));
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ this.FadeInFromZero(500, Easing.OutQuint);
+ }
+
private string createUrl(string source)
{
if (string.IsNullOrEmpty(source))
diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs
index 900b4bebf0..949f1e7b96 100644
--- a/osu.Game/Overlays/NowPlayingOverlay.cs
+++ b/osu.Game/Overlays/NowPlayingOverlay.cs
@@ -100,7 +100,7 @@ namespace osu.Game.Overlays
},
Children = new[]
{
- background = new Background(),
+ background = Empty(),
title = new OsuSpriteText
{
Origin = Anchor.BottomCentre,
@@ -413,7 +413,7 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
- private void load(TextureStore textures)
+ private void load(LargeTextureStore textures)
{
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
}
diff --git a/osu.Game/Overlays/OverlayHeaderBackground.cs b/osu.Game/Overlays/OverlayHeaderBackground.cs
index c47f16272f..540b28d9b2 100644
--- a/osu.Game/Overlays/OverlayHeaderBackground.cs
+++ b/osu.Game/Overlays/OverlayHeaderBackground.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Overlays
Height = 80;
RelativeSizeAxes = Axes.X;
Masking = true;
- InternalChild = new Background(textureName);
+ InternalChild = new DelayedLoadWrapper(() => new Background(textureName));
}
private class Background : Sprite
@@ -36,10 +36,16 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
- private void load(TextureStore textures)
+ private void load(LargeTextureStore textures)
{
Texture = textures.Get(textureName);
}
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ this.FadeInFromZero(500, Easing.OutQuint);
+ }
}
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs
index b92746a65a..2f4840a384 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs
@@ -3,6 +3,8 @@
#nullable disable
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Localisation;
@@ -13,6 +15,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
protected override LocalisableString Header => BindingSettingsStrings.ShortcutAndGameplayBindings;
+ public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "keybindings" });
+
public BindingSettings(KeyBindingPanel keyConfig)
{
Children = new Drawable[]
diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
index 59c7ff04a2..de52a1f938 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
@@ -111,9 +111,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows || RuntimeInfo.OS == RuntimeInfo.Platform.Linux)
{
t.NewLine();
- var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedBindableString(TabletSettingsStrings.NoTabletDetectedDescription(RuntimeInfo.OS == RuntimeInfo.Platform.Windows
- ? @"https://opentabletdriver.net/Wiki/FAQ/Windows"
- : @"https://opentabletdriver.net/Wiki/FAQ/Linux")).Value);
+ var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedBindableString(TabletSettingsStrings.NoTabletDetectedDescription(
+ RuntimeInfo.OS == RuntimeInfo.Platform.Windows
+ ? @"https://opentabletdriver.net/Wiki/FAQ/Windows"
+ : @"https://opentabletdriver.net/Wiki/FAQ/Linux")).Value);
t.AddLinks(formattedSource.Text, formattedSource.Links);
}
}),
@@ -274,6 +275,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
sizeY.Default = sizeY.MaxValue = tab.Size.Y;
areaSize.Default = new Vector2(sizeX.Default, sizeY.Default);
+ areaOffset.Default = new Vector2(offsetX.Default, offsetY.Default);
}), true);
}
diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs
index a8fe3d04be..4d75537f6b 100644
--- a/osu.Game/Overlays/Settings/Sections/InputSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs
@@ -3,8 +3,6 @@
#nullable disable
-using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
@@ -22,8 +20,6 @@ namespace osu.Game.Overlays.Settings.Sections
public override LocalisableString Header => InputSettingsStrings.InputSectionHeader;
- public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "keybindings" });
-
public override Drawable CreateIcon() => new SpriteIcon
{
Icon = FontAwesome.Solid.Keyboard
diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs
index ad52b4affc..c50f63c3b2 100644
--- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs
+++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs
@@ -115,7 +115,11 @@ namespace osu.Game.Rulesets.UI
public Sample Get(string name) => primary.Get(name) ?? fallback.Get(name);
- public Task GetAsync(string name, CancellationToken cancellationToken = default) => primary.GetAsync(name, cancellationToken) ?? fallback.GetAsync(name, cancellationToken);
+ public async Task GetAsync(string name, CancellationToken cancellationToken = default)
+ {
+ return await primary.GetAsync(name, cancellationToken).ConfigureAwait(false)
+ ?? await fallback.GetAsync(name, cancellationToken).ConfigureAwait(false);
+ }
public Stream GetStream(string name) => primary.GetStream(name) ?? fallback.GetStream(name);
diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
index 3d18b00e75..d3c330c6d7 100644
--- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
+++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
@@ -11,6 +11,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
+using osu.Game.Configuration;
using osu.Game.Extensions;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
@@ -19,7 +20,7 @@ using osu.Game.Screens.Edit.Compose.Components.Timeline;
namespace osu.Game.Screens.Edit.Compose
{
- public class ComposeScreen : EditorScreenWithTimeline
+ public class ComposeScreen : EditorScreenWithTimeline, IGameplaySettings
{
[Resolved]
private GameHost host { get; set; }
@@ -27,6 +28,9 @@ namespace osu.Game.Screens.Edit.Compose
[Resolved]
private EditorClock clock { get; set; }
+ [Resolved]
+ private IGameplaySettings globalGameplaySettings { get; set; }
+
private Bindable clipboard { get; set; }
private HitObjectComposer composer;
@@ -157,5 +161,12 @@ namespace osu.Game.Screens.Edit.Compose
}
#endregion
+
+ // Combo colour normalisation should not be applied in the editor.
+ // Note this doesn't affect editor test mode.
+ IBindable IGameplaySettings.ComboColourNormalisationAmount => new Bindable();
+
+ // Arguable.
+ IBindable IGameplaySettings.PositionalHitsoundsLevel => globalGameplaySettings.PositionalHitsoundsLevel;
}
}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index bb390dfbf3..4c44117581 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -58,8 +58,7 @@ namespace osu.Game.Screens.Edit
{
[Cached(typeof(IBeatSnapProvider))]
[Cached]
- public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IKeyBindingHandler, IBeatSnapProvider, ISamplePlaybackDisabler, IBeatSyncProvider,
- IGameplaySettings
+ public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IKeyBindingHandler, IBeatSnapProvider, ISamplePlaybackDisabler, IBeatSyncProvider
{
public override float BackgroundParallaxAmount => 0.1f;
@@ -99,9 +98,6 @@ namespace osu.Game.Screens.Edit
[Resolved(canBeNull: true)]
private INotificationOverlay notifications { get; set; }
- [Resolved]
- private IGameplaySettings globalGameplaySettings { get; set; }
-
public readonly Bindable Mode = new Bindable();
public IBindable SamplePlaybackDisabled => samplePlaybackDisabled;
@@ -1045,11 +1041,5 @@ namespace osu.Game.Screens.Edit
{
}
}
-
- // Combo colour normalisation should not be applied in the editor.
- IBindable IGameplaySettings.ComboColourNormalisationAmount => new Bindable();
-
- // Arguable.
- IBindable IGameplaySettings.PositionalHitsoundsLevel => globalGameplaySettings.PositionalHitsoundsLevel;
}
}
diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs
index 94975b6b5e..251feecf28 100644
--- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs
+++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs
@@ -1,8 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
@@ -17,7 +16,7 @@ namespace osu.Game.Screens.Edit.GameplayTest
private readonly EditorState editorState;
[Resolved]
- private MusicController musicController { get; set; }
+ private MusicController musicController { get; set; } = null!;
public EditorPlayer(Editor editor)
: base(new PlayerConfiguration { ShowResults = false })
@@ -29,7 +28,12 @@ namespace osu.Game.Screens.Edit.GameplayTest
protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart)
{
var masterGameplayClockContainer = new MasterGameplayClockContainer(beatmap, gameplayStart);
- masterGameplayClockContainer.Reset(editorState.Time);
+
+ // Only reset the time to the current point if the editor is later than the normal start time (and the first object).
+ // This allows more sane test playing from the start of the beatmap (ie. correctly adding lead-in time).
+ if (editorState.Time > gameplayStart && editorState.Time > DrawableRuleset.Objects.FirstOrDefault()?.StartTime)
+ masterGameplayClockContainer.Reset(editorState.Time);
+
return masterGameplayClockContainer;
}
diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs
index afba00274c..ac22fdce71 100644
--- a/osu.Game/Screens/Loader.cs
+++ b/osu.Game/Screens/Loader.cs
@@ -125,13 +125,11 @@ namespace osu.Game.Screens
[BackgroundDependencyLoader]
private void load(ShaderManager manager)
{
- loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED));
- loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE));
+ loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR));
loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE));
- loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE_ROUNDED));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE));
}
diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs
index 05a6d25303..4ec877b85a 100644
--- a/osu.Game/Screens/Menu/IntroTriangles.cs
+++ b/osu.Game/Screens/Menu/IntroTriangles.cs
@@ -224,8 +224,8 @@ namespace osu.Game.Screens.Menu
{
rulesetsScale.ScaleTo(0.8f, 1000);
rulesets.FadeIn().ScaleTo(1).TransformSpacingTo(new Vector2(200, 0));
- welcomeText.FadeOut();
- triangles.FadeOut();
+ welcomeText.FadeOut().Expire();
+ triangles.FadeOut().Expire();
}
using (BeginDelayedSequence(rulesets_2))
@@ -307,7 +307,7 @@ namespace osu.Game.Screens.Menu
}
[BackgroundDependencyLoader]
- private void load(TextureStore textures)
+ private void load(LargeTextureStore textures)
{
InternalChildren = new Drawable[]
{
diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs
index c7bda4d8f8..4a20d7cb2b 100644
--- a/osu.Game/Screens/Menu/LogoVisualisation.cs
+++ b/osu.Game/Screens/Menu/LogoVisualisation.cs
@@ -89,7 +89,7 @@ namespace osu.Game.Screens.Menu
private void load(IRenderer renderer, ShaderManager shaders)
{
texture = renderer.WhitePixel;
- shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
+ shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
}
private readonly float[] temporalAmplitudes = new float[ChannelAmplitudes.AMPLITUDES_SIZE];
diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
index e7b2ce1672..c9f1571dfe 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Linq;
using osu.Framework.Allocation;
@@ -11,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Pooling;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
@@ -23,10 +22,9 @@ using osuTK;
namespace osu.Game.Screens.Play.HUD.HitErrorMeters
{
+ [Cached]
public class BarHitErrorMeter : HitErrorMeter
{
- private const int judgement_line_width = 14;
-
[SettingSource("Judgement line thickness", "How thick the individual lines should be.")]
public BindableNumber JudgementLineThickness { get; } = new BindableNumber(4)
{
@@ -44,28 +42,33 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
[SettingSource("Label style", "How to show early/late extremities")]
public Bindable LabelStyle { get; } = new Bindable(LabelStyles.Icons);
- private SpriteIcon arrow;
- private UprightAspectMaintainingContainer labelEarly;
- private UprightAspectMaintainingContainer labelLate;
+ private const int judgement_line_width = 14;
- private Container colourBarsEarly;
- private Container colourBarsLate;
+ private const int max_concurrent_judgements = 50;
- private Container judgementsContainer;
+ private const int centre_marker_size = 8;
private double maxHitWindow;
private double floatingAverage;
- private Container colourBars;
- private Container arrowContainer;
- private (HitResult result, double length)[] hitWindows;
+ private readonly DrawablePool judgementLinePool = new DrawablePool(50);
- private const int max_concurrent_judgements = 50;
+ private SpriteIcon arrow = null!;
+ private UprightAspectMaintainingContainer labelEarly = null!;
+ private UprightAspectMaintainingContainer labelLate = null!;
- private Drawable[] centreMarkerDrawables;
+ private Container colourBarsEarly = null!;
+ private Container colourBarsLate = null!;
- private const int centre_marker_size = 8;
+ private Container judgementsContainer = null!;
+
+ private Container colourBars = null!;
+ private Container arrowContainer = null!;
+
+ private (HitResult result, double length)[] hitWindows = null!;
+
+ private Drawable[]? centreMarkerDrawables;
public BarHitErrorMeter()
{
@@ -88,6 +91,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
Margin = new MarginPadding(2),
Children = new Drawable[]
{
+ judgementLinePool,
colourBars = new Container
{
Name = "colour axis",
@@ -403,11 +407,12 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
}
}
- judgementsContainer.Add(new JudgementLine
+ judgementLinePool.Get(drawableJudgement =>
{
- JudgementLineThickness = { BindTarget = JudgementLineThickness },
- Y = getRelativeJudgementPosition(judgement.TimeOffset),
- Colour = GetColourForHitResult(judgement.Type),
+ drawableJudgement.Y = getRelativeJudgementPosition(judgement.TimeOffset);
+ drawableJudgement.Colour = GetColourForHitResult(judgement.Type);
+
+ judgementsContainer.Add(drawableJudgement);
});
arrow.MoveToY(
@@ -417,10 +422,13 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
private float getRelativeJudgementPosition(double value) => Math.Clamp((float)((value / maxHitWindow) + 1) / 2, 0, 1);
- internal class JudgementLine : CompositeDrawable
+ internal class JudgementLine : PoolableDrawable
{
public readonly BindableNumber JudgementLineThickness = new BindableFloat();
+ [Resolved]
+ private BarHitErrorMeter barHitErrorMeter { get; set; } = null!;
+
public JudgementLine()
{
RelativeSizeAxes = Axes.X;
@@ -439,16 +447,22 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
protected override void LoadComplete()
{
+ base.LoadComplete();
+
+ JudgementLineThickness.BindTo(barHitErrorMeter.JudgementLineThickness);
+ JudgementLineThickness.BindValueChanged(thickness => Height = thickness.NewValue, true);
+ }
+
+ protected override void PrepareForUse()
+ {
+ base.PrepareForUse();
+
const int judgement_fade_in_duration = 100;
const int judgement_fade_out_duration = 5000;
- base.LoadComplete();
-
Alpha = 0;
Width = 0;
- JudgementLineThickness.BindValueChanged(thickness => Height = thickness.NewValue, true);
-
this
.FadeTo(0.6f, judgement_fade_in_duration, Easing.OutQuint)
.ResizeWidthTo(1, judgement_fade_in_duration, Easing.OutQuint)
diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs
index dadec7c06b..86ba85168f 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs
@@ -3,9 +3,11 @@
using System.Collections.Generic;
using System.Linq;
+using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Pooling;
using osu.Framework.Graphics.Shapes;
using osu.Game.Configuration;
using osu.Game.Rulesets.Judgements;
@@ -15,6 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Screens.Play.HUD.HitErrorMeters
{
+ [Cached]
public class ColourHitErrorMeter : HitErrorMeter
{
private const int animation_duration = 200;
@@ -82,7 +85,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
{
base.LoadComplete();
- JudgementCount.BindValueChanged(count =>
+ JudgementCount.BindValueChanged(_ =>
{
removeExtraJudgements();
updateMetrics();
@@ -91,14 +94,17 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
JudgementSpacing.BindValueChanged(_ => updateMetrics(), true);
}
+ private readonly DrawablePool judgementLinePool = new DrawablePool(50);
+
public void Push(Color4 colour)
{
- Add(new HitErrorShape(colour, drawable_judgement_size)
+ judgementLinePool.Get(shape =>
{
- Shape = { BindTarget = JudgementShape },
- });
+ shape.Colour = colour;
+ Add(shape);
- removeExtraJudgements();
+ removeExtraJudgements();
+ });
}
private void removeExtraJudgements()
@@ -116,32 +122,32 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
}
}
- public class HitErrorShape : Container
+ public class HitErrorShape : PoolableDrawable
{
public bool IsRemoved { get; private set; }
public readonly Bindable Shape = new Bindable();
- private readonly Color4 colour;
+ [Resolved]
+ private ColourHitErrorMeter hitErrorMeter { get; set; } = null!;
private Container content = null!;
- public HitErrorShape(Color4 colour, int size)
+ public HitErrorShape()
{
- this.colour = colour;
- Size = new Vector2(size);
+ Size = new Vector2(drawable_judgement_size);
}
protected override void LoadComplete()
{
base.LoadComplete();
- Child = content = new Container
+ InternalChild = content = new Container
{
RelativeSizeAxes = Axes.Both,
- Colour = colour
};
+ Shape.BindTo(hitErrorMeter.JudgementShape);
Shape.BindValueChanged(shape =>
{
switch (shape.NewValue)
@@ -155,17 +161,32 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
break;
}
}, true);
+ }
- content.FadeInFromZero(animation_duration, Easing.OutQuint);
- content.MoveToY(-DrawSize.Y);
- content.MoveToY(0, animation_duration, Easing.OutQuint);
+ protected override void PrepareForUse()
+ {
+ base.PrepareForUse();
+
+ this.FadeInFromZero(animation_duration, Easing.OutQuint)
+ // On pool re-use, start flow animation from (0,0).
+ .MoveTo(Vector2.Zero);
+
+ content.MoveToY(-DrawSize.Y)
+ .MoveToY(0, animation_duration, Easing.OutQuint);
+ }
+
+ protected override void FreeAfterUse()
+ {
+ base.FreeAfterUse();
+ IsRemoved = false;
}
public void Remove()
{
IsRemoved = true;
- this.FadeOut(animation_duration, Easing.OutQuint).Expire();
+ this.FadeOut(animation_duration, Easing.OutQuint)
+ .Expire();
}
}
diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs
index 86fe76c0c6..f9fc2890b0 100644
--- a/osu.Game/Screens/Select/Footer.cs
+++ b/osu.Game/Screens/Select/Footer.cs
@@ -57,7 +57,18 @@ namespace osu.Game.Screens.Select
}
}
- private void updateModeLight() => modeLight.FadeColour(buttons.FirstOrDefault(b => b.IsHovered)?.SelectedColour ?? Color4.Transparent, TRANSITION_LENGTH, Easing.OutQuint);
+ private void updateModeLight()
+ {
+ var selectedButton = buttons.FirstOrDefault(b => b.Enabled.Value && b.IsHovered);
+
+ if (selectedButton != null)
+ {
+ modeLight.FadeIn(TRANSITION_LENGTH, Easing.OutQuint);
+ modeLight.FadeColour(selectedButton.SelectedColour, TRANSITION_LENGTH, Easing.OutQuint);
+ }
+ else
+ modeLight.FadeOut(TRANSITION_LENGTH, Easing.OutQuint);
+ }
public Footer()
{
@@ -78,6 +89,7 @@ namespace osu.Game.Screens.Select
RelativeSizeAxes = Axes.X,
Height = 3,
Position = new Vector2(0, -3),
+ Colour = Color4.Black,
},
new FillFlowContainer
{
diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs
index 3f8cf2e13a..230cdfc13e 100644
--- a/osu.Game/Screens/Select/FooterButton.cs
+++ b/osu.Game/Screens/Select/FooterButton.cs
@@ -120,10 +120,18 @@ namespace osu.Game.Screens.Select
};
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ Enabled.BindValueChanged(_ => updateDisplay(), true);
+ }
+
public Action Hovered;
public Action HoverLost;
public GlobalAction? Hotkey;
+ private bool mouseDown;
+
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
@@ -140,32 +148,38 @@ namespace osu.Game.Screens.Select
protected override bool OnHover(HoverEvent e)
{
Hovered?.Invoke();
- light.ScaleTo(new Vector2(1, 2), Footer.TRANSITION_LENGTH, Easing.OutQuint);
- light.FadeColour(SelectedColour, Footer.TRANSITION_LENGTH, Easing.OutQuint);
+ updateDisplay();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
HoverLost?.Invoke();
- light.ScaleTo(new Vector2(1, 1), Footer.TRANSITION_LENGTH, Easing.OutQuint);
- light.FadeColour(DeselectedColour, Footer.TRANSITION_LENGTH, Easing.OutQuint);
+ updateDisplay();
}
protected override bool OnMouseDown(MouseDownEvent e)
{
- box.FadeTo(0.3f, Footer.TRANSITION_LENGTH * 2, Easing.OutQuint);
+ if (!Enabled.Value)
+ return true;
+
+ mouseDown = true;
+ updateDisplay();
return base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseUpEvent e)
{
- box.FadeOut(Footer.TRANSITION_LENGTH, Easing.OutQuint);
+ mouseDown = false;
+ updateDisplay();
base.OnMouseUp(e);
}
protected override bool OnClick(ClickEvent e)
{
+ if (!Enabled.Value)
+ return true;
+
box.ClearTransforms();
box.Alpha = 1;
box.FadeOut(Footer.TRANSITION_LENGTH * 3, Easing.OutQuint);
@@ -184,5 +198,20 @@ namespace osu.Game.Screens.Select
}
public virtual void OnReleased(KeyBindingReleaseEvent e) { }
+
+ private void updateDisplay()
+ {
+ this.FadeTo(Enabled.Value ? 1 : 0.25f, Footer.TRANSITION_LENGTH, Easing.OutQuint);
+
+ light.ScaleTo(Enabled.Value && IsHovered ? new Vector2(1, 2) : new Vector2(1), Footer.TRANSITION_LENGTH, Easing.OutQuint);
+ light.FadeColour(Enabled.Value && IsHovered ? SelectedColour : DeselectedColour, Footer.TRANSITION_LENGTH, Easing.OutQuint);
+
+ box.FadeTo(Enabled.Value & mouseDown ? 0.3f : 0f, Footer.TRANSITION_LENGTH * 2, Easing.OutQuint);
+
+ if (Enabled.Value && IsHovered)
+ Hovered?.Invoke();
+ else
+ HoverLost?.Invoke();
+ }
}
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 1add51e725..5d5019567a 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -112,6 +112,8 @@ namespace osu.Game.Screens.Select
protected BeatmapDetailArea BeatmapDetails { get; private set; }
+ private FooterButtonOptions beatmapOptionsButton;
+
private readonly Bindable decoupledRuleset = new Bindable();
private double audioFeedbackLastPlaybackTime;
@@ -314,7 +316,7 @@ namespace osu.Game.Screens.Select
NextRandom = () => Carousel.SelectNextRandom(),
PreviousRandom = Carousel.SelectPreviousRandom
}, null),
- (new FooterButtonOptions(), BeatmapOptions)
+ (beatmapOptionsButton = new FooterButtonOptions(), BeatmapOptions)
};
protected virtual ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay();
@@ -739,6 +741,16 @@ namespace osu.Game.Screens.Select
beatmapInfoWedge.Beatmap = beatmap;
BeatmapDetails.Beatmap = beatmap;
+
+ bool beatmapSelected = beatmap is not DummyWorkingBeatmap;
+
+ if (beatmapSelected)
+ beatmapOptionsButton.Enabled.Value = true;
+ else
+ {
+ beatmapOptionsButton.Enabled.Value = false;
+ BeatmapOptions.Hide();
+ }
}
private readonly WeakReference lastTrack = new WeakReference(null);
diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs
index a2eb07eba3..6a0c4a23e5 100644
--- a/osu.Game/Skinning/ArgonSkin.cs
+++ b/osu.Game/Skinning/ArgonSkin.cs
@@ -82,21 +82,14 @@ namespace osu.Game.Skinning
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
{
+ // Temporary until default skin has a valid hit lighting.
+ if ((lookup as SkinnableSprite.SpriteComponentLookup)?.LookupName == @"lighting") return Drawable.Empty();
+
if (base.GetDrawableComponent(lookup) is Drawable c)
return c;
switch (lookup)
{
- case SkinnableSprite.SpriteComponentLookup spriteLookup:
- switch (spriteLookup.LookupName)
- {
- // Temporary until default skin has a valid hit lighting.
- case @"lighting":
- return Drawable.Empty();
- }
-
- break;
-
case GlobalSkinComponentLookup globalLookup:
switch (globalLookup.Lookup)
{
diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs
index 98618e3dcd..ea223d172d 100644
--- a/osu.Game/Skinning/LegacySkin.cs
+++ b/osu.Game/Skinning/LegacySkin.cs
@@ -396,9 +396,6 @@ namespace osu.Game.Skinning
}
return null;
-
- case SkinnableSprite.SpriteComponentLookup sprite:
- return this.GetAnimation(sprite.LookupName, false, false);
}
return null;
diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs
index e222b9017c..25d1dc903c 100644
--- a/osu.Game/Skinning/Skin.cs
+++ b/osu.Game/Skinning/Skin.cs
@@ -158,6 +158,10 @@ namespace osu.Game.Skinning
{
switch (lookup)
{
+ // This fallback is important for user skins which use SkinnableSprites.
+ case SkinnableSprite.SpriteComponentLookup sprite:
+ return this.GetAnimation(sprite.LookupName, false, false);
+
case GlobalSkinComponentLookup target:
if (!DrawableComponentInfo.TryGetValue(target.Lookup, out var skinnableInfo))
return null;
diff --git a/osu.Game/Skinning/TrianglesSkin.cs b/osu.Game/Skinning/TrianglesSkin.cs
index 2075cfb6f2..62ef94691b 100644
--- a/osu.Game/Skinning/TrianglesSkin.cs
+++ b/osu.Game/Skinning/TrianglesSkin.cs
@@ -60,21 +60,14 @@ namespace osu.Game.Skinning
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
{
+ // Temporary until default skin has a valid hit lighting.
+ if ((lookup as SkinnableSprite.SpriteComponentLookup)?.LookupName == @"lighting") return Drawable.Empty();
+
if (base.GetDrawableComponent(lookup) is Drawable c)
return c;
switch (lookup)
{
- case SkinnableSprite.SpriteComponentLookup spriteLookup:
- switch (spriteLookup.LookupName)
- {
- // Temporary until default skin has a valid hit lighting.
- case @"lighting":
- return Drawable.Empty();
- }
-
- break;
-
case GlobalSkinComponentLookup target:
switch (target.Lookup)
{
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 09b1bb7162..858cac1dac 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -35,8 +35,8 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 4264d9220e..25217b872b 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -61,8 +61,8 @@
-
-
+
+
@@ -82,7 +82,7 @@
-
+