Skip to content

Commit

Permalink
Merge pull request #29542 from frenzibyte/show-daily-challenge-intro-…
Browse files Browse the repository at this point in the history
…once-per-session

Show daily challenge intro screen once per session
  • Loading branch information
peppy authored Sep 1, 2024
2 parents 6c5ce36 + b544870 commit e79604c
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Configuration;
using osu.Game.Online.API;
using osu.Game.Online.Metadata;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Rulesets.Osu;
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.OnlinePlay;
using osuTK.Graphics;
using osuTK.Input;
using CreateRoomRequest = osu.Game.Online.Rooms.CreateRoomRequest;

namespace osu.Game.Tests.Visual.DailyChallenge
Expand All @@ -27,63 +29,61 @@ public partial class TestSceneDailyChallengeIntro : OnlinePlayTestScene
[Cached(typeof(INotificationOverlay))]
private NotificationOverlay notificationOverlay = new NotificationOverlay();

private Room room = null!;

[BackgroundDependencyLoader]
private void load()
{
base.Content.Add(notificationOverlay);
base.Content.Add(metadataClient);
Add(notificationOverlay);
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]
[Solo]
public void TestDailyChallenge()
{
var room = new 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)));
startChallenge(1234);
AddStep("push screen", () => LoadScreen(new DailyChallengeIntro(room)));
}

[Test]
public void TestNotifications()
public void TestPlayIntroOnceFlag()
{
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)
{
var room = new Room
AddStep("add room", () =>
{
RoomID = { Value = 1234 },
Name = { Value = "Daily Challenge: June 4, 2024" },
Playlist =
API.Perform(new CreateRoomRequest(room = new Room
{
new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First())
RoomID = { Value = roomId },
Name = { Value = "Daily Challenge: June 4, 2024" },
Playlist =
{
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("set daily challenge info", () => metadataClient.DailyChallengeInfo.Value = new DailyChallengeInfo { RoomID = 1234 });

Screens.OnlinePlay.DailyChallenge.DailyChallenge screen = null!;
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);
new PlaylistItem(CreateAPIBeatmap(new OsuRuleset().RulesetInfo))
{
RequiredMods = [new APIMod(new OsuModTraceable())],
AllowedMods = [new APIMod(new OsuModDoubleTime())]
}
},
StartDate = { Value = DateTimeOffset.Now },
EndDate = { Value = DateTimeOffset.Now.AddHours(24) },
Category = { Value = RoomCategory.DailyChallenge }
}));
});
AddStep("signal client", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo { RoomID = roomId }));
}
}
}
64 changes: 64 additions & 0 deletions osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,79 @@ public void TestDailyChallengeButton()
foreach (var notification in notificationOverlay.AllNotifications)
notification.Close(runFlingAnimation: false);
});

AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null));
AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero);

AddStep("hide button's parent", () => buttonContainer.Hide());

AddStep("beatmap of the day active", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo
{
RoomID = 1234,
}));
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);
}
}
}
6 changes: 6 additions & 0 deletions osu.Game/Configuration/SessionStatics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,11 @@ public enum Static
/// Stores the local user's last score (can be completed or aborted).
/// </summary>
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,
}
}
21 changes: 14 additions & 7 deletions osu.Game/Screens/Menu/DailyChallengeButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using osu.Framework.Threading;
using osu.Framework.Utils;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Localisation;
Expand Down Expand Up @@ -46,6 +47,9 @@ public partial class DailyChallengeButton : MainMenuButton
[Resolved]
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)
: base(ButtonSystemStrings.DailyChallenge, sampleName, OsuIcon.DailyChallenge, colour, clickAction, triggerKeys)
{
Expand Down Expand Up @@ -128,7 +132,7 @@ protected override void Update()
}
}

private long? lastNotifiedDailyChallengeRoomId;
private long? lastDailyChallengeRoomID;

private void dailyChallengeChanged(ValueChangedEvent<DailyChallengeInfo?> _)
{
Expand All @@ -151,13 +155,16 @@ private void dailyChallengeChanged(ValueChangedEvent<DailyChallengeInfo?> _)
Room = room;
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
&& Math.Abs((DateTimeOffset.Now - room.StartDate.Value!.Value).TotalSeconds) < 1800
&& room.RoomID.Value != lastNotifiedDailyChallengeRoomId)
if (room.StartDate.Value != null && room.RoomID.Value != lastDailyChallengeRoomID)
{
lastNotifiedDailyChallengeRoomId = room.RoomID.Value;
notificationOverlay?.Post(new NewDailyChallengeNotification(room));
lastDailyChallengeRoomID = room.RoomID.Value;

// 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();
Expand Down
5 changes: 4 additions & 1 deletion osu.Game/Screens/Menu/MainMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,10 @@ private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings
OnPlaylists = () => this.Push(new Playlists()),
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 = () =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public partial class DailyChallengeIntro : OsuScreen
[Resolved]
private MusicController musicController { get; set; } = null!;

[Resolved]
private SessionStatics statics { get; set; } = null!;

private Sample? dateWindupSample;
private Sample? dateImpactSample;
private Sample? beatmapWindupSample;
Expand Down Expand Up @@ -462,6 +465,8 @@ private void beginAnimation()
{
Schedule(() =>
{
statics.SetValue(Static.DailyChallengeIntroPlayed, true);

if (this.IsCurrentScreen())
this.Push(new DailyChallenge(room));
});
Expand Down
10 changes: 9 additions & 1 deletion osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Tests.Beatmaps;
using osu.Game.Utils;

namespace osu.Game.Tests.Visual.OnlinePlay
{
Expand Down Expand Up @@ -278,11 +279,18 @@ private Room cloneRoom(Room source)
var result = JsonConvert.DeserializeObject<Room>(JsonConvert.SerializeObject(source));
Debug.Assert(result != null);

// Playlist item IDs aren't serialised.
// Playlist item IDs and beatmaps aren't serialised.
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;
}

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;
}

return result;
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game/Utils/Optional.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public readonly ref struct Optional<T>
/// </remarks>
public readonly bool HasValue;

private Optional(T value)
public Optional(T value)
{
Value = value;
HasValue = true;
Expand Down

0 comments on commit e79604c

Please sign in to comment.