1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-18 21:02:56 +08:00

Merge pull request #29542 from frenzibyte/show-daily-challenge-intro-once-per-session

Show daily challenge intro screen once per session
This commit is contained in:
Dean Herbert 2024-09-01 21:27:16 +09:00 committed by GitHub
commit e79604cc13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 149 additions and 56 deletions

View File

@ -2,19 +2,21 @@
// 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 System; using System;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Screens; using osu.Game.Configuration;
using osu.Framework.Testing;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.Metadata; using osu.Game.Online.Metadata;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Tests.Resources; using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.DailyChallenge;
using osu.Game.Tests.Visual.Metadata; using osu.Game.Tests.Visual.Metadata;
using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Graphics;
using osuTK.Input;
using CreateRoomRequest = osu.Game.Online.Rooms.CreateRoomRequest; using CreateRoomRequest = osu.Game.Online.Rooms.CreateRoomRequest;
namespace osu.Game.Tests.Visual.DailyChallenge namespace osu.Game.Tests.Visual.DailyChallenge
@ -27,63 +29,61 @@ namespace osu.Game.Tests.Visual.DailyChallenge
[Cached(typeof(INotificationOverlay))] [Cached(typeof(INotificationOverlay))]
private NotificationOverlay notificationOverlay = new NotificationOverlay(); private NotificationOverlay notificationOverlay = new NotificationOverlay();
private Room room = null!;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
base.Content.Add(notificationOverlay); Add(notificationOverlay);
base.Content.Add(metadataClient); Add(metadataClient);
// add button to observe for daily challenge changes and perform its logic.
Add(new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), _ => { }, 0, Key.D));
} }
[Test] [Test]
[Solo]
public void TestDailyChallenge() public void TestDailyChallenge()
{ {
var room = new Room startChallenge(1234);
{ AddStep("push screen", () => LoadScreen(new DailyChallengeIntro(room)));
RoomID = { Value = 1234 },
Name = { Value = "Daily Challenge: June 4, 2024" },
Playlist =
{
new PlaylistItem(CreateAPIBeatmapSet().Beatmaps.First())
{
RequiredMods = [new APIMod(new OsuModTraceable())],
AllowedMods = [new APIMod(new OsuModDoubleTime())]
}
},
EndDate = { Value = DateTimeOffset.Now.AddHours(12) },
Category = { Value = RoomCategory.DailyChallenge }
};
AddStep("add room", () => API.Perform(new CreateRoomRequest(room)));
AddStep("push screen", () => LoadScreen(new Screens.OnlinePlay.DailyChallenge.DailyChallengeIntro(room)));
} }
[Test] [Test]
public void TestNotifications() public void TestPlayIntroOnceFlag()
{ {
var room = new Room startChallenge(1234);
AddStep("set intro played flag", () => Dependencies.Get<SessionStatics>().SetValue(Static.DailyChallengeIntroPlayed, true));
startChallenge(1235);
AddAssert("intro played flag reset", () => Dependencies.Get<SessionStatics>().Get<bool>(Static.DailyChallengeIntroPlayed), () => Is.False);
AddStep("push screen", () => LoadScreen(new DailyChallengeIntro(room)));
AddUntilStep("intro played flag set", () => Dependencies.Get<SessionStatics>().Get<bool>(Static.DailyChallengeIntroPlayed), () => Is.True);
}
private void startChallenge(int roomId)
{
AddStep("add room", () =>
{ {
RoomID = { Value = 1234 }, API.Perform(new CreateRoomRequest(room = new Room
Name = { Value = "Daily Challenge: June 4, 2024" },
Playlist =
{ {
new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First()) RoomID = { Value = roomId },
Name = { Value = "Daily Challenge: June 4, 2024" },
Playlist =
{ {
RequiredMods = [new APIMod(new OsuModTraceable())], new PlaylistItem(CreateAPIBeatmap(new OsuRuleset().RulesetInfo))
AllowedMods = [new APIMod(new OsuModDoubleTime())] {
} RequiredMods = [new APIMod(new OsuModTraceable())],
}, AllowedMods = [new APIMod(new OsuModDoubleTime())]
EndDate = { Value = DateTimeOffset.Now.AddHours(12) }, }
Category = { Value = RoomCategory.DailyChallenge } },
}; StartDate = { Value = DateTimeOffset.Now },
EndDate = { Value = DateTimeOffset.Now.AddHours(24) },
AddStep("add room", () => API.Perform(new CreateRoomRequest(room))); Category = { Value = RoomCategory.DailyChallenge }
AddStep("set daily challenge info", () => metadataClient.DailyChallengeInfo.Value = new DailyChallengeInfo { RoomID = 1234 }); }));
});
Screens.OnlinePlay.DailyChallenge.DailyChallenge screen = null!; AddStep("signal client", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo { RoomID = roomId }));
AddStep("push screen", () => LoadScreen(screen = new Screens.OnlinePlay.DailyChallenge.DailyChallenge(room)));
AddUntilStep("wait for screen", () => screen.IsCurrentScreen());
AddStep("daily challenge ended", () => metadataClient.DailyChallengeInfo.Value = null);
} }
} }
} }

View File

@ -103,15 +103,79 @@ namespace osu.Game.Tests.Visual.UserInterface
foreach (var notification in notificationOverlay.AllNotifications) foreach (var notification in notificationOverlay.AllNotifications)
notification.Close(runFlingAnimation: false); notification.Close(runFlingAnimation: false);
}); });
AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null)); AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null));
AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero); AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero);
AddStep("hide button's parent", () => buttonContainer.Hide()); AddStep("hide button's parent", () => buttonContainer.Hide());
AddStep("beatmap of the day active", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo AddStep("beatmap of the day active", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo
{ {
RoomID = 1234, RoomID = 1234,
})); }));
AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero); AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero);
} }
[Test]
public void TestDailyChallengeButtonOldChallenge()
{
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)
},
StartDate = { Value = DateTimeOffset.Now.AddMinutes(-50) },
EndDate = { Value = DateTimeOffset.Now.AddSeconds(30) }
});
return true;
default:
return false;
}
});
NotificationOverlay notificationOverlay = null!;
AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null));
AddStep("add content", () =>
{
notificationOverlay = new NotificationOverlay();
Children = new Drawable[]
{
notificationOverlay,
new DependencyProvidingContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
CachedDependencies = [(typeof(INotificationOverlay), notificationOverlay)],
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
}));
AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero);
}
} }
} }

View File

@ -80,5 +80,11 @@ namespace osu.Game.Configuration
/// Stores the local user's last score (can be completed or aborted). /// Stores the local user's last score (can be completed or aborted).
/// </summary> /// </summary>
LastLocalUserScore, LastLocalUserScore,
/// <summary>
/// Whether the intro animation for the daily challenge screen has been played once.
/// This is reset when a new challenge is up.
/// </summary>
DailyChallengeIntroPlayed,
} }
} }

View File

@ -13,6 +13,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Localisation; using osu.Game.Localisation;
@ -46,6 +47,9 @@ namespace osu.Game.Screens.Menu
[Resolved] [Resolved]
private INotificationOverlay? notificationOverlay { get; set; } private INotificationOverlay? notificationOverlay { get; set; }
[Resolved]
private SessionStatics statics { get; set; } = null!;
public DailyChallengeButton(string sampleName, Color4 colour, Action<MainMenuButton>? clickAction = null, params Key[] triggerKeys) public DailyChallengeButton(string sampleName, Color4 colour, Action<MainMenuButton>? clickAction = null, params Key[] triggerKeys)
: base(ButtonSystemStrings.DailyChallenge, sampleName, OsuIcon.DailyChallenge, colour, clickAction, triggerKeys) : base(ButtonSystemStrings.DailyChallenge, sampleName, OsuIcon.DailyChallenge, colour, clickAction, triggerKeys)
{ {
@ -128,7 +132,7 @@ namespace osu.Game.Screens.Menu
} }
} }
private long? lastNotifiedDailyChallengeRoomId; private long? lastDailyChallengeRoomID;
private void dailyChallengeChanged(ValueChangedEvent<DailyChallengeInfo?> _) private void dailyChallengeChanged(ValueChangedEvent<DailyChallengeInfo?> _)
{ {
@ -151,13 +155,16 @@ namespace osu.Game.Screens.Menu
Room = room; Room = room;
cover.OnlineInfo = TooltipContent = room.Playlist.FirstOrDefault()?.Beatmap.BeatmapSet as APIBeatmapSet; cover.OnlineInfo = TooltipContent = room.Playlist.FirstOrDefault()?.Beatmap.BeatmapSet as APIBeatmapSet;
// We only want to notify the user if a new challenge recently went live. if (room.StartDate.Value != null && room.RoomID.Value != lastDailyChallengeRoomID)
if (room.StartDate.Value != null
&& Math.Abs((DateTimeOffset.Now - room.StartDate.Value!.Value).TotalSeconds) < 1800
&& room.RoomID.Value != lastNotifiedDailyChallengeRoomId)
{ {
lastNotifiedDailyChallengeRoomId = room.RoomID.Value; lastDailyChallengeRoomID = room.RoomID.Value;
notificationOverlay?.Post(new NewDailyChallengeNotification(room));
// new challenge is live, reset intro played static.
statics.SetValue(Static.DailyChallengeIntroPlayed, false);
// we only want to notify the user if the new challenge just went live.
if (Math.Abs((DateTimeOffset.Now - room.StartDate.Value!.Value).TotalSeconds) < 1800)
notificationOverlay?.Post(new NewDailyChallengeNotification(room));
} }
updateCountdown(); updateCountdown();

View File

@ -148,7 +148,10 @@ namespace osu.Game.Screens.Menu
OnPlaylists = () => this.Push(new Playlists()), OnPlaylists = () => this.Push(new Playlists()),
OnDailyChallenge = room => OnDailyChallenge = room =>
{ {
this.Push(new DailyChallengeIntro(room)); if (statics.Get<bool>(Static.DailyChallengeIntroPlayed))
this.Push(new DailyChallenge(room));
else
this.Push(new DailyChallengeIntro(room));
}, },
OnExit = () => OnExit = () =>
{ {

View File

@ -70,6 +70,9 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
[Resolved] [Resolved]
private MusicController musicController { get; set; } = null!; private MusicController musicController { get; set; } = null!;
[Resolved]
private SessionStatics statics { get; set; } = null!;
private Sample? dateWindupSample; private Sample? dateWindupSample;
private Sample? dateImpactSample; private Sample? dateImpactSample;
private Sample? beatmapWindupSample; private Sample? beatmapWindupSample;
@ -462,6 +465,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
{ {
Schedule(() => Schedule(() =>
{ {
statics.SetValue(Static.DailyChallengeIntroPlayed, true);
if (this.IsCurrentScreen()) if (this.IsCurrentScreen())
this.Push(new DailyChallenge(room)); this.Push(new DailyChallenge(room));
}); });

View File

@ -19,6 +19,7 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using osu.Game.Utils;
namespace osu.Game.Tests.Visual.OnlinePlay namespace osu.Game.Tests.Visual.OnlinePlay
{ {
@ -278,11 +279,18 @@ namespace osu.Game.Tests.Visual.OnlinePlay
var result = JsonConvert.DeserializeObject<Room>(JsonConvert.SerializeObject(source)); var result = JsonConvert.DeserializeObject<Room>(JsonConvert.SerializeObject(source));
Debug.Assert(result != null); Debug.Assert(result != null);
// Playlist item IDs aren't serialised. // Playlist item IDs and beatmaps aren't serialised.
if (source.CurrentPlaylistItem.Value != null) if (source.CurrentPlaylistItem.Value != null)
{
result.CurrentPlaylistItem.Value = result.CurrentPlaylistItem.Value.With(new Optional<IBeatmapInfo>(source.CurrentPlaylistItem.Value.Beatmap));
result.CurrentPlaylistItem.Value.ID = source.CurrentPlaylistItem.Value.ID; result.CurrentPlaylistItem.Value.ID = source.CurrentPlaylistItem.Value.ID;
}
for (int i = 0; i < source.Playlist.Count; i++) for (int i = 0; i < source.Playlist.Count; i++)
{
result.Playlist[i] = result.Playlist[i].With(new Optional<IBeatmapInfo>(source.Playlist[i].Beatmap));
result.Playlist[i].ID = source.Playlist[i].ID; result.Playlist[i].ID = source.Playlist[i].ID;
}
return result; return result;
} }

View File

@ -22,7 +22,7 @@ namespace osu.Game.Utils
/// </remarks> /// </remarks>
public readonly bool HasValue; public readonly bool HasValue;
private Optional(T value) public Optional(T value)
{ {
Value = value; Value = value;
HasValue = true; HasValue = true;