mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 08:32:57 +08:00
Merge pull request #28195 from bdach/daily-challenge-mvp
Minimum viable prototype of "daily challenge" feature
This commit is contained in:
commit
f78c17c9f5
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.Metadata;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
|
using osuTK.Input;
|
||||||
|
using Color4 = osuTK.Graphics.Color4;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public partial class TestSceneMainMenuButton : OsuTestScene
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private MetadataClient metadataClient { get; set; } = null!;
|
||||||
|
|
||||||
|
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStandardButton()
|
||||||
|
{
|
||||||
|
AddStep("add button", () => Child = new MainMenuButton(
|
||||||
|
ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), _ => { }, 0, Key.P)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
ButtonSystemState = ButtonSystemState.TopLevel,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDailyChallengeButton()
|
||||||
|
{
|
||||||
|
AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null));
|
||||||
|
|
||||||
|
AddStep("set up API", () => dummyAPI.HandleRequest = req =>
|
||||||
|
{
|
||||||
|
switch (req)
|
||||||
|
{
|
||||||
|
case GetRoomRequest getRoomRequest:
|
||||||
|
if (getRoomRequest.RoomId != 1234)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var beatmap = CreateAPIBeatmap();
|
||||||
|
beatmap.OnlineID = 1001;
|
||||||
|
getRoomRequest.TriggerSuccess(new Room
|
||||||
|
{
|
||||||
|
RoomID = { Value = 1234 },
|
||||||
|
Playlist =
|
||||||
|
{
|
||||||
|
new PlaylistItem(beatmap)
|
||||||
|
},
|
||||||
|
EndDate = { Value = DateTimeOffset.Now.AddSeconds(30) }
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("add button", () => Child = new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), _ => { }, 0, Key.D)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
ButtonSystemState = ButtonSystemState.TopLevel,
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("beatmap of the day active", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo
|
||||||
|
{
|
||||||
|
RoomID = 1234,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,8 +27,17 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
set => base.Masking = value;
|
set => base.Masking = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType coverType = BeatmapSetCoverType.Cover)
|
protected override double LoadDelay { get; }
|
||||||
|
|
||||||
|
private readonly double timeBeforeUnload;
|
||||||
|
|
||||||
|
protected override double TransformDuration => 400;
|
||||||
|
|
||||||
|
public UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType coverType = BeatmapSetCoverType.Cover, double timeBeforeLoad = 500, double timeBeforeUnload = 1000)
|
||||||
{
|
{
|
||||||
|
LoadDelay = timeBeforeLoad;
|
||||||
|
this.timeBeforeUnload = timeBeforeUnload;
|
||||||
|
|
||||||
this.coverType = coverType;
|
this.coverType = coverType;
|
||||||
|
|
||||||
InternalChild = new Box
|
InternalChild = new Box
|
||||||
@ -38,12 +47,12 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override double LoadDelay => 500;
|
|
||||||
|
|
||||||
protected override double TransformDuration => 400;
|
|
||||||
|
|
||||||
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
|
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
|
||||||
=> new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad);
|
=> new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, timeBeforeUnload)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
};
|
||||||
|
|
||||||
protected override Drawable CreateDrawable(IBeatmapSetOnlineInfo model)
|
protected override Drawable CreateDrawable(IBeatmapSetOnlineInfo model)
|
||||||
{
|
{
|
||||||
|
@ -120,6 +120,7 @@ namespace osu.Game.Graphics
|
|||||||
public static IconUsage Cross => get(OsuIconMapping.Cross);
|
public static IconUsage Cross => get(OsuIconMapping.Cross);
|
||||||
public static IconUsage CrossCircle => get(OsuIconMapping.CrossCircle);
|
public static IconUsage CrossCircle => get(OsuIconMapping.CrossCircle);
|
||||||
public static IconUsage Crown => get(OsuIconMapping.Crown);
|
public static IconUsage Crown => get(OsuIconMapping.Crown);
|
||||||
|
public static IconUsage DailyChallenge => get(OsuIconMapping.DailyChallenge);
|
||||||
public static IconUsage Debug => get(OsuIconMapping.Debug);
|
public static IconUsage Debug => get(OsuIconMapping.Debug);
|
||||||
public static IconUsage Delete => get(OsuIconMapping.Delete);
|
public static IconUsage Delete => get(OsuIconMapping.Delete);
|
||||||
public static IconUsage Details => get(OsuIconMapping.Details);
|
public static IconUsage Details => get(OsuIconMapping.Details);
|
||||||
@ -218,6 +219,9 @@ namespace osu.Game.Graphics
|
|||||||
[Description(@"crown")]
|
[Description(@"crown")]
|
||||||
Crown,
|
Crown,
|
||||||
|
|
||||||
|
[Description(@"daily-challenge")]
|
||||||
|
DailyChallenge,
|
||||||
|
|
||||||
[Description(@"debug")]
|
[Description(@"debug")]
|
||||||
Debug,
|
Debug,
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
@ -54,6 +54,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString Exit => new TranslatableString(getKey(@"exit"), @"exit");
|
public static LocalisableString Exit => new TranslatableString(getKey(@"exit"), @"exit");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "daily challenge"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DailyChallenge => new TranslatableString(getKey(@"daily_challenge"), @"daily challenge");
|
||||||
|
|
||||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ using osu.Game.Input;
|
|||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -46,6 +47,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
public Action? OnSettings;
|
public Action? OnSettings;
|
||||||
public Action? OnMultiplayer;
|
public Action? OnMultiplayer;
|
||||||
public Action? OnPlaylists;
|
public Action? OnPlaylists;
|
||||||
|
public Action<Room>? OnDailyChallenge;
|
||||||
|
|
||||||
private readonly IBindable<bool> isIdle = new BindableBool();
|
private readonly IBindable<bool> isIdle = new BindableBool();
|
||||||
|
|
||||||
@ -102,10 +104,13 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
buttonArea.AddRange(new Drawable[]
|
buttonArea.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, OsuIcon.Settings, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O, Key.S),
|
new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, OsuIcon.Settings, new Color4(85, 85, 85, 255), _ => OnSettings?.Invoke(), Key.O, Key.S)
|
||||||
backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.PrevCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel,
|
|
||||||
-WEDGE_WIDTH)
|
|
||||||
{
|
{
|
||||||
|
Padding = new MarginPadding { Right = WEDGE_WIDTH },
|
||||||
|
},
|
||||||
|
backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.PrevCircle, new Color4(51, 58, 94, 255), _ => State = ButtonSystemState.TopLevel)
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding { Right = WEDGE_WIDTH },
|
||||||
VisibleStateMin = ButtonSystemState.Play,
|
VisibleStateMin = ButtonSystemState.Play,
|
||||||
VisibleStateMax = ButtonSystemState.Edit,
|
VisibleStateMax = ButtonSystemState.Edit,
|
||||||
},
|
},
|
||||||
@ -127,21 +132,31 @@ namespace osu.Game.Screens.Menu
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, IdleTracker? idleTracker, GameHost host)
|
private void load(AudioManager audio, IdleTracker? idleTracker, GameHost host)
|
||||||
{
|
{
|
||||||
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
|
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), _ => OnSolo?.Invoke(), Key.P)
|
||||||
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", OsuIcon.Online, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M));
|
{
|
||||||
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Tournament, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L));
|
Padding = new MarginPadding { Left = WEDGE_WIDTH },
|
||||||
|
});
|
||||||
|
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", OsuIcon.Online, new Color4(94, 63, 186, 255), onMultiplayer, Key.M));
|
||||||
|
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Tournament, new Color4(94, 63, 186, 255), onPlaylists, Key.L));
|
||||||
|
buttonsPlay.Add(new DailyChallengeButton(@"button-default-select", new Color4(94, 63, 186, 255), onDailyChallenge, Key.D));
|
||||||
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
||||||
|
|
||||||
buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), () => OnEditBeatmap?.Invoke(), WEDGE_WIDTH, Key.B, Key.E));
|
buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), _ => OnEditBeatmap?.Invoke(), Key.B, Key.E)
|
||||||
buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", OsuIcon.SkinB, new Color4(220, 160, 0, 255), () => OnEditSkin?.Invoke(), 0, Key.S));
|
{
|
||||||
|
Padding = new MarginPadding { Left = WEDGE_WIDTH },
|
||||||
|
});
|
||||||
|
buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", OsuIcon.SkinB, new Color4(220, 160, 0, 255), _ => OnEditSkin?.Invoke(), Key.S));
|
||||||
buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit);
|
buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit);
|
||||||
|
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P, Key.M, Key.L));
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), _ => State = ButtonSystemState.Play, Key.P, Key.M, Key.L)
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-play-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => State = ButtonSystemState.Edit, 0, Key.E));
|
{
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.Beatmap, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D));
|
Padding = new MarginPadding { Left = WEDGE_WIDTH },
|
||||||
|
});
|
||||||
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-play-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), _ => State = ButtonSystemState.Edit, Key.E));
|
||||||
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.Beatmap, new Color4(165, 204, 0, 255), _ => OnBeatmapListing?.Invoke(), Key.B, Key.D));
|
||||||
|
|
||||||
if (host.CanExit)
|
if (host.CanExit)
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q));
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), _ => OnExit?.Invoke(), Key.Q));
|
||||||
|
|
||||||
buttonArea.AddRange(buttonsPlay);
|
buttonArea.AddRange(buttonsPlay);
|
||||||
buttonArea.AddRange(buttonsEdit);
|
buttonArea.AddRange(buttonsEdit);
|
||||||
@ -164,7 +179,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
sampleLogoSwoosh = audio.Samples.Get(@"Menu/osu-logo-swoosh");
|
sampleLogoSwoosh = audio.Samples.Get(@"Menu/osu-logo-swoosh");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMultiplayer()
|
private void onMultiplayer(MainMenuButton _)
|
||||||
{
|
{
|
||||||
if (api.State.Value != APIState.Online)
|
if (api.State.Value != APIState.Online)
|
||||||
{
|
{
|
||||||
@ -175,7 +190,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
OnMultiplayer?.Invoke();
|
OnMultiplayer?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPlaylists()
|
private void onPlaylists(MainMenuButton _)
|
||||||
{
|
{
|
||||||
if (api.State.Value != APIState.Online)
|
if (api.State.Value != APIState.Online)
|
||||||
{
|
{
|
||||||
@ -186,6 +201,20 @@ namespace osu.Game.Screens.Menu
|
|||||||
OnPlaylists?.Invoke();
|
OnPlaylists?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onDailyChallenge(MainMenuButton button)
|
||||||
|
{
|
||||||
|
if (api.State.Value != APIState.Online)
|
||||||
|
{
|
||||||
|
loginOverlay?.Show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dailyChallengeButton = (DailyChallengeButton)button;
|
||||||
|
|
||||||
|
if (dailyChallengeButton.Room != null)
|
||||||
|
OnDailyChallenge?.Invoke(dailyChallengeButton.Room);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateIdleState(bool isIdle)
|
private void updateIdleState(bool isIdle)
|
||||||
{
|
{
|
||||||
if (!ReturnToTopOnIdle)
|
if (!ReturnToTopOnIdle)
|
||||||
|
221
osu.Game/Screens/Menu/DailyChallengeButton.cs
Normal file
221
osu.Game/Screens/Menu/DailyChallengeButton.cs
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps.Drawables;
|
||||||
|
using osu.Game.Beatmaps.Drawables.Cards;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Online.Metadata;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Menu
|
||||||
|
{
|
||||||
|
public partial class DailyChallengeButton : MainMenuButton, IHasCustomTooltip<APIBeatmapSet?>
|
||||||
|
{
|
||||||
|
public Room? Room { get; private set; }
|
||||||
|
|
||||||
|
private readonly OsuSpriteText countdown;
|
||||||
|
private ScheduledDelegate? scheduledCountdownUpdate;
|
||||||
|
|
||||||
|
private UpdateableOnlineBeatmapSetCover cover = null!;
|
||||||
|
private IBindable<DailyChallengeInfo?> info = null!;
|
||||||
|
|
||||||
|
private Box gradientLayer = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; } = null!;
|
||||||
|
|
||||||
|
public DailyChallengeButton(string sampleName, Color4 colour, Action<MainMenuButton>? clickAction = null, params Key[] triggerKeys)
|
||||||
|
: base(ButtonSystemStrings.DailyChallenge, sampleName, OsuIcon.DailyChallenge, colour, clickAction, triggerKeys)
|
||||||
|
{
|
||||||
|
BaseSize = new Vector2(ButtonSystem.BUTTON_WIDTH * 1.3f, ButtonArea.BUTTON_AREA_HEIGHT);
|
||||||
|
|
||||||
|
Content.Add(countdown = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Shadow = true,
|
||||||
|
AllowMultiline = false,
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Left = -3,
|
||||||
|
Bottom = 22,
|
||||||
|
},
|
||||||
|
Font = OsuFont.Default.With(size: 12),
|
||||||
|
Alpha = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Drawable CreateBackground(Colour4 accentColour) => new BufferedContainer
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
cover = new UpdateableOnlineBeatmapSetCover(timeBeforeLoad: 0, timeBeforeUnload: 600_000)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativePositionAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
gradientLayer = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourInfo.GradientVertical(accentColour.Opacity(0.2f), accentColour),
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = accentColour.Opacity(0.7f)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(MetadataClient metadataClient)
|
||||||
|
{
|
||||||
|
info = metadataClient.DailyChallengeInfo.GetBoundCopy();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
info.BindValueChanged(updateDisplay, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (cover.LatestTransformEndTime == Time.Current)
|
||||||
|
{
|
||||||
|
const double duration = 3000;
|
||||||
|
|
||||||
|
float scale = 1 + RNG.NextSingle();
|
||||||
|
|
||||||
|
cover.ScaleTo(scale, duration, Easing.InOutSine)
|
||||||
|
.RotateTo(RNG.NextSingle(-4, 4) * (scale - 1), duration, Easing.InOutSine)
|
||||||
|
.MoveTo(new Vector2(
|
||||||
|
RNG.NextSingle(-0.5f, 0.5f) * (scale - 1),
|
||||||
|
RNG.NextSingle(-0.5f, 0.5f) * (scale - 1)
|
||||||
|
), duration, Easing.InOutSine);
|
||||||
|
|
||||||
|
gradientLayer.FadeIn(duration / 2)
|
||||||
|
.Then()
|
||||||
|
.FadeOut(duration / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDisplay(ValueChangedEvent<DailyChallengeInfo?> info)
|
||||||
|
{
|
||||||
|
UpdateState();
|
||||||
|
|
||||||
|
scheduledCountdownUpdate?.Cancel();
|
||||||
|
scheduledCountdownUpdate = null;
|
||||||
|
|
||||||
|
if (info.NewValue == null)
|
||||||
|
{
|
||||||
|
Room = null;
|
||||||
|
cover.OnlineInfo = TooltipContent = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var roomRequest = new GetRoomRequest(info.NewValue.Value.RoomID);
|
||||||
|
|
||||||
|
roomRequest.Success += room =>
|
||||||
|
{
|
||||||
|
Room = room;
|
||||||
|
cover.OnlineInfo = TooltipContent = room.Playlist.FirstOrDefault()?.Beatmap.BeatmapSet as APIBeatmapSet;
|
||||||
|
|
||||||
|
updateCountdown();
|
||||||
|
Scheduler.AddDelayed(updateCountdown, 1000, true);
|
||||||
|
};
|
||||||
|
api.Queue(roomRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCountdown()
|
||||||
|
{
|
||||||
|
if (Room == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var remaining = (Room.EndDate.Value - DateTimeOffset.Now) ?? TimeSpan.Zero;
|
||||||
|
|
||||||
|
if (remaining <= TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
countdown.FadeOut(250, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (countdown.Alpha == 0)
|
||||||
|
countdown.FadeIn(250, Easing.OutQuint);
|
||||||
|
|
||||||
|
countdown.Text = remaining.ToString(@"hh\:mm\:ss");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState()
|
||||||
|
{
|
||||||
|
if (info.IsNotNull() && info.Value == null)
|
||||||
|
{
|
||||||
|
ContractStyle = 0;
|
||||||
|
State = ButtonState.Contracted;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.UpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITooltip<APIBeatmapSet?> GetCustomTooltip() => new DailyChallengeTooltip();
|
||||||
|
|
||||||
|
public APIBeatmapSet? TooltipContent { get; private set; }
|
||||||
|
|
||||||
|
internal partial class DailyChallengeTooltip : CompositeDrawable, ITooltip<APIBeatmapSet?>
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||||
|
|
||||||
|
private APIBeatmapSet? lastContent;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Move(Vector2 pos) => Position = pos;
|
||||||
|
|
||||||
|
public void SetContent(APIBeatmapSet? content)
|
||||||
|
{
|
||||||
|
if (content == lastContent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lastContent = content;
|
||||||
|
|
||||||
|
ClearInternal();
|
||||||
|
if (content != null)
|
||||||
|
AddInternal(new BeatmapCardNano(content));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -147,6 +147,12 @@ namespace osu.Game.Screens.Menu
|
|||||||
OnSolo = loadSoloSongSelect,
|
OnSolo = loadSoloSongSelect,
|
||||||
OnMultiplayer = () => this.Push(new Multiplayer()),
|
OnMultiplayer = () => this.Push(new Multiplayer()),
|
||||||
OnPlaylists = () => this.Push(new Playlists()),
|
OnPlaylists = () => this.Push(new Playlists()),
|
||||||
|
OnDailyChallenge = room =>
|
||||||
|
{
|
||||||
|
Playlists playlistsScreen;
|
||||||
|
this.Push(playlistsScreen = new Playlists());
|
||||||
|
playlistsScreen.Join(room);
|
||||||
|
},
|
||||||
OnExit = () =>
|
OnExit = () =>
|
||||||
{
|
{
|
||||||
exitConfirmedViaHoldOrClick = true;
|
exitConfirmedViaHoldOrClick = true;
|
||||||
|
@ -38,11 +38,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
public readonly Key[] TriggerKeys;
|
public readonly Key[] TriggerKeys;
|
||||||
|
|
||||||
private readonly Container iconText;
|
protected override Container<Drawable> Content => content;
|
||||||
private readonly Container box;
|
private readonly Container content;
|
||||||
private readonly Box boxHoverLayer;
|
|
||||||
private readonly SpriteIcon icon;
|
|
||||||
private readonly string sampleName;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The menu state for which we are visible for (assuming only one).
|
/// The menu state for which we are visible for (assuming only one).
|
||||||
@ -59,7 +56,24 @@ namespace osu.Game.Screens.Menu
|
|||||||
public ButtonSystemState VisibleStateMin = ButtonSystemState.TopLevel;
|
public ButtonSystemState VisibleStateMin = ButtonSystemState.TopLevel;
|
||||||
public ButtonSystemState VisibleStateMax = ButtonSystemState.TopLevel;
|
public ButtonSystemState VisibleStateMax = ButtonSystemState.TopLevel;
|
||||||
|
|
||||||
private readonly Action? clickAction;
|
public new MarginPadding Padding
|
||||||
|
{
|
||||||
|
get => Content.Padding;
|
||||||
|
set => Content.Padding = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Vector2 BaseSize { get; init; } = new Vector2(ButtonSystem.BUTTON_WIDTH, ButtonArea.BUTTON_AREA_HEIGHT);
|
||||||
|
|
||||||
|
private readonly Action<MainMenuButton>? clickAction;
|
||||||
|
|
||||||
|
private readonly Container background;
|
||||||
|
private readonly Drawable backgroundContent;
|
||||||
|
private readonly Box boxHoverLayer;
|
||||||
|
private readonly SpriteIcon icon;
|
||||||
|
|
||||||
|
private Vector2 initialSize => BaseSize + Padding.Total;
|
||||||
|
|
||||||
|
private readonly string sampleName;
|
||||||
private Sample? sampleClick;
|
private Sample? sampleClick;
|
||||||
private Sample? sampleHover;
|
private Sample? sampleHover;
|
||||||
private SampleChannel? sampleChannel;
|
private SampleChannel? sampleChannel;
|
||||||
@ -68,9 +82,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
// Allow keyboard interaction based on state rather than waiting for delayed animations.
|
// Allow keyboard interaction based on state rather than waiting for delayed animations.
|
||||||
|| state == ButtonState.Expanded;
|
|| state == ButtonState.Expanded;
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => background.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
public MainMenuButton(LocalisableString text, string sampleName, IconUsage symbol, Color4 colour, Action? clickAction = null, float extraWidth = 0, params Key[] triggerKeys)
|
public MainMenuButton(LocalisableString text, string sampleName, IconUsage symbol, Color4 colour, Action<MainMenuButton>? clickAction = null, params Key[] triggerKeys)
|
||||||
{
|
{
|
||||||
this.sampleName = sampleName;
|
this.sampleName = sampleName;
|
||||||
this.clickAction = clickAction;
|
this.clickAction = clickAction;
|
||||||
@ -79,11 +93,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
|
|
||||||
Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonArea.BUTTON_AREA_HEIGHT);
|
AddRangeInternal(new Drawable[]
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
box = new Container
|
background = new Container
|
||||||
{
|
{
|
||||||
// box needs to be always present to ensure the button is always sized correctly for flow
|
// box needs to be always present to ensure the button is always sized correctly for flow
|
||||||
AlwaysPresent = true,
|
AlwaysPresent = true,
|
||||||
@ -98,35 +110,46 @@ namespace osu.Game.Screens.Menu
|
|||||||
},
|
},
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Scale = new Vector2(0, 1),
|
|
||||||
Size = boxSize,
|
|
||||||
Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / boxSize.Y, 0),
|
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new Box
|
backgroundContent = CreateBackground(colour).With(bg =>
|
||||||
{
|
{
|
||||||
EdgeSmoothness = new Vector2(1.5f, 0),
|
bg.RelativeSizeAxes = Axes.Y;
|
||||||
RelativeSizeAxes = Axes.Both,
|
bg.X = -ButtonSystem.WEDGE_WIDTH;
|
||||||
Colour = colour,
|
bg.Anchor = Anchor.Centre;
|
||||||
},
|
bg.Origin = Anchor.Centre;
|
||||||
|
}),
|
||||||
boxHoverLayer = new Box
|
boxHoverLayer = new Box
|
||||||
{
|
{
|
||||||
EdgeSmoothness = new Vector2(1.5f, 0),
|
EdgeSmoothness = new Vector2(1.5f, 0),
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
Colour = Color4.White,
|
Colour = Color4.White,
|
||||||
|
Depth = float.MinValue,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
iconText = new Container
|
content = new Container
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Position = new Vector2(extraWidth / 2, 0),
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Shadow = true,
|
||||||
|
AllowMultiline = false,
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Left = -3,
|
||||||
|
Bottom = 7,
|
||||||
|
},
|
||||||
|
Text = text
|
||||||
|
},
|
||||||
icon = new SpriteIcon
|
icon = new SpriteIcon
|
||||||
{
|
{
|
||||||
Shadow = true,
|
Shadow = true,
|
||||||
@ -136,20 +159,38 @@ namespace osu.Game.Screens.Menu
|
|||||||
Position = new Vector2(0, 0),
|
Position = new Vector2(0, 0),
|
||||||
Margin = new MarginPadding { Top = -4 },
|
Margin = new MarginPadding { Top = -4 },
|
||||||
Icon = symbol
|
Icon = symbol
|
||||||
},
|
|
||||||
new OsuSpriteText
|
|
||||||
{
|
|
||||||
Shadow = true,
|
|
||||||
AllowMultiline = false,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Position = new Vector2(0, 35),
|
|
||||||
Margin = new MarginPadding { Left = -3 },
|
|
||||||
Text = text
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual Drawable CreateBackground(Colour4 accentColour) => new Container
|
||||||
|
{
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
EdgeSmoothness = new Vector2(1.5f, 0),
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = accentColour,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
background.Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / initialSize.Y, 0);
|
||||||
|
|
||||||
|
// for whatever reason, attempting to size the background "just in time" to cover the visible width
|
||||||
|
// results in gaps when the width changes are quick (only visible when testing menu at 100% speed, not visible slowed down).
|
||||||
|
// to ensure there's no missing backdrop, just use a ballpark that should be enough to always cover the width and then some.
|
||||||
|
// note that while on a code inspections it would seem that `1.5 * initialSize.X` would be enough, elastic usings are used in this button
|
||||||
|
// (which can exceed the [0;1] range during interpolation).
|
||||||
|
backgroundContent.Width = 2 * initialSize.X;
|
||||||
|
backgroundContent.Shear = -background.Shear;
|
||||||
|
|
||||||
|
animateState();
|
||||||
|
FinishTransforms(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool rightward;
|
private bool rightward;
|
||||||
@ -179,15 +220,15 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
if (State != ButtonState.Expanded) return true;
|
if (State != ButtonState.Expanded) return true;
|
||||||
|
|
||||||
sampleHover?.Play();
|
|
||||||
|
|
||||||
box.ScaleTo(new Vector2(1.5f, 1), 500, Easing.OutElastic);
|
|
||||||
|
|
||||||
double duration = TimeUntilNextBeat;
|
double duration = TimeUntilNextBeat;
|
||||||
|
|
||||||
icon.ClearTransforms();
|
icon.ClearTransforms();
|
||||||
icon.RotateTo(rightward ? -BOUNCE_ROTATION : BOUNCE_ROTATION, duration, Easing.InOutSine);
|
icon.RotateTo(rightward ? -BOUNCE_ROTATION : BOUNCE_ROTATION, duration, Easing.InOutSine);
|
||||||
icon.ScaleTo(new Vector2(HOVER_SCALE, HOVER_SCALE * BOUNCE_COMPRESSION), duration, Easing.Out);
|
icon.ScaleTo(new Vector2(HOVER_SCALE, HOVER_SCALE * BOUNCE_COMPRESSION), duration, Easing.Out);
|
||||||
|
|
||||||
|
sampleHover?.Play();
|
||||||
|
background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(1.5f, 1)), 500, Easing.OutElastic);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +240,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
icon.ScaleTo(Vector2.One, 200, Easing.Out);
|
icon.ScaleTo(Vector2.One, 200, Easing.Out);
|
||||||
|
|
||||||
if (State == ButtonState.Expanded)
|
if (State == ButtonState.Expanded)
|
||||||
box.ScaleTo(new Vector2(1, 1), 500, Easing.OutElastic);
|
background.ResizeTo(initialSize, 500, Easing.OutElastic);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -246,7 +287,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
sampleChannel = sampleClick?.GetChannel();
|
sampleChannel = sampleClick?.GetChannel();
|
||||||
sampleChannel?.Play();
|
sampleChannel?.Play();
|
||||||
|
|
||||||
clickAction?.Invoke();
|
clickAction?.Invoke(this);
|
||||||
|
|
||||||
boxHoverLayer.ClearTransforms();
|
boxHoverLayer.ClearTransforms();
|
||||||
boxHoverLayer.Alpha = 0.9f;
|
boxHoverLayer.Alpha = 0.9f;
|
||||||
@ -254,13 +295,13 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override bool HandleNonPositionalInput => state == ButtonState.Expanded;
|
public override bool HandleNonPositionalInput => state == ButtonState.Expanded;
|
||||||
public override bool HandlePositionalInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f;
|
public override bool HandlePositionalInput => state != ButtonState.Exploded && background.Width / initialSize.X >= 0.8f;
|
||||||
|
|
||||||
public void StopSamplePlayback() => sampleChannel?.Stop();
|
public void StopSamplePlayback() => sampleChannel?.Stop();
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
iconText.Alpha = Math.Clamp((box.Scale.X - 0.5f) / 0.3f, 0, 1);
|
content.Alpha = Math.Clamp((background.Width / initialSize.X - 0.5f) / 0.3f, 0, 1);
|
||||||
base.Update();
|
base.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,67 +320,84 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
state = value;
|
state = value;
|
||||||
|
|
||||||
switch (state)
|
animateState();
|
||||||
{
|
|
||||||
case ButtonState.Contracted:
|
|
||||||
switch (ContractStyle)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
box.ScaleTo(new Vector2(0, 1), 500, Easing.OutExpo);
|
|
||||||
this.FadeOut(500);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
box.ScaleTo(new Vector2(0, 1), 400, Easing.InSine);
|
|
||||||
this.FadeOut(800);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ButtonState.Expanded:
|
|
||||||
const int expand_duration = 500;
|
|
||||||
box.ScaleTo(new Vector2(1, 1), expand_duration, Easing.OutExpo);
|
|
||||||
this.FadeIn(expand_duration / 6f);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ButtonState.Exploded:
|
|
||||||
const int explode_duration = 200;
|
|
||||||
box.ScaleTo(new Vector2(2, 1), explode_duration, Easing.OutExpo);
|
|
||||||
this.FadeOut(explode_duration / 4f * 3);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateChanged?.Invoke(State);
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void animateState()
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case ButtonState.Contracted:
|
||||||
|
switch (ContractStyle)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(0, 1)), 500, Easing.OutExpo);
|
||||||
|
this.FadeOut(500);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(0, 1)), 400, Easing.InSine);
|
||||||
|
this.FadeOut(800);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ButtonState.Expanded:
|
||||||
|
const int expand_duration = 500;
|
||||||
|
background.ResizeTo(initialSize, expand_duration, Easing.OutExpo);
|
||||||
|
this.FadeIn(expand_duration / 6f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ButtonState.Exploded:
|
||||||
|
const int explode_duration = 200;
|
||||||
|
background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(2, 1)), explode_duration, Easing.OutExpo);
|
||||||
|
this.FadeOut(explode_duration / 4f * 3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ButtonSystemState buttonSystemState;
|
||||||
|
|
||||||
public ButtonSystemState ButtonSystemState
|
public ButtonSystemState ButtonSystemState
|
||||||
{
|
{
|
||||||
|
get => buttonSystemState;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ContractStyle = 0;
|
if (buttonSystemState == value)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (value)
|
buttonSystemState = value;
|
||||||
{
|
UpdateState();
|
||||||
case ButtonSystemState.Initial:
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateState()
|
||||||
|
{
|
||||||
|
ContractStyle = 0;
|
||||||
|
|
||||||
|
switch (ButtonSystemState)
|
||||||
|
{
|
||||||
|
case ButtonSystemState.Initial:
|
||||||
|
State = ButtonState.Contracted;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ButtonSystemState.EnteringMode:
|
||||||
|
ContractStyle = 1;
|
||||||
|
State = ButtonState.Contracted;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (ButtonSystemState <= VisibleStateMax && ButtonSystemState >= VisibleStateMin)
|
||||||
|
State = ButtonState.Expanded;
|
||||||
|
else if (ButtonSystemState < VisibleStateMin)
|
||||||
State = ButtonState.Contracted;
|
State = ButtonState.Contracted;
|
||||||
break;
|
else
|
||||||
|
State = ButtonState.Exploded;
|
||||||
case ButtonSystemState.EnteringMode:
|
break;
|
||||||
ContractStyle = 1;
|
|
||||||
State = ButtonState.Contracted;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (value <= VisibleStateMax && value >= VisibleStateMin)
|
|
||||||
State = ButtonState.Expanded;
|
|
||||||
else if (value < VisibleStateMin)
|
|
||||||
State = ButtonState.Contracted;
|
|
||||||
else
|
|
||||||
State = ButtonState.Exploded;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||||
@ -10,5 +11,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
protected override string ScreenTitle => "Playlists";
|
protected override string ScreenTitle => "Playlists";
|
||||||
|
|
||||||
protected override LoungeSubScreen CreateLounge() => new PlaylistsLoungeSubScreen();
|
protected override LoungeSubScreen CreateLounge() => new PlaylistsLoungeSubScreen();
|
||||||
|
|
||||||
|
public void Join(Room room) => Schedule(() => Lounge.Join(room, string.Empty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="11.5.0" />
|
<PackageReference Include="Realm" Version="11.5.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2024.523.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2024.523.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.510.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.517.0" />
|
||||||
<PackageReference Include="Sentry" Version="4.3.0" />
|
<PackageReference Include="Sentry" Version="4.3.0" />
|
||||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||||
<PackageReference Include="SharpCompress" Version="0.36.0" />
|
<PackageReference Include="SharpCompress" Version="0.36.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user