2024-02-20 19:53:46 +08:00
// 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.
2024-02-20 20:08:02 +08:00
using System.Collections.Generic ;
2024-02-20 19:53:46 +08:00
using osu.Framework.Allocation ;
using osu.Framework.Extensions.ObjectExtensions ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Input.Events ;
2024-08-23 18:49:15 +08:00
using osu.Framework.Logging ;
2024-02-20 19:53:46 +08:00
using osu.Game.Graphics.Containers ;
using osu.Game.Input.Bindings ;
using osu.Game.Online.API ;
using osu.Game.Online.Notifications.WebSocket ;
using osu.Game.Online.Notifications.WebSocket.Events ;
using osu.Game.Users ;
namespace osu.Game.Overlays
{
public partial class MedalOverlay : OsuFocusedOverlayContainer
{
protected override string? PopInSampleName = > null ;
protected override string? PopOutSampleName = > null ;
2024-02-20 22:02:30 +08:00
public override bool IsPresent = > base . IsPresent | | Scheduler . HasPendingTasks ;
2024-02-20 19:53:46 +08:00
protected override void PopIn ( ) = > this . FadeIn ( ) ;
2024-02-20 20:44:25 +08:00
protected override void PopOut ( ) = > this . FadeOut ( ) ;
2024-02-20 19:53:46 +08:00
2024-02-20 20:08:02 +08:00
private readonly Queue < MedalAnimation > queuedMedals = new Queue < MedalAnimation > ( ) ;
2024-02-20 19:53:46 +08:00
[Resolved]
private IAPIProvider api { get ; set ; } = null ! ;
private Container < Drawable > medalContainer = null ! ;
2024-11-26 13:13:07 +08:00
private MedalAnimation ? currentMedalDisplay ;
2024-02-20 19:53:46 +08:00
[BackgroundDependencyLoader]
private void load ( )
{
RelativeSizeAxes = Axes . Both ;
api . NotificationsClient . MessageReceived + = handleMedalMessages ;
Add ( medalContainer = new Container
{
RelativeSizeAxes = Axes . Both
} ) ;
}
2024-02-20 20:44:25 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2024-11-26 16:58:50 +08:00
OverlayActivationMode . BindValueChanged ( _ = > showNextMedal ( ) , true ) ;
2024-02-20 20:44:25 +08:00
}
2024-11-26 13:42:30 +08:00
public override void Hide ( )
{
// don't allow hiding the overlay via any method other than our own.
}
2024-02-20 19:53:46 +08:00
private void handleMedalMessages ( SocketMessage obj )
{
if ( obj . Event ! = @"new" )
return ;
var data = obj . Data ? . ToObject < NewPrivateNotificationEvent > ( ) ;
if ( data = = null | | data . Name ! = @"user_achievement_unlock" )
return ;
var details = data . Details ? . ToObject < UserAchievementUnlock > ( ) ;
if ( details = = null )
return ;
var medal = new Medal
{
Name = details . Title ,
InternalName = details . Slug ,
Description = details . Description ,
} ;
2024-02-20 20:44:25 +08:00
var medalAnimation = new MedalAnimation ( medal ) ;
2024-08-23 18:49:15 +08:00
Logger . Log ( $"Queueing medal unlock for \" { medal . Name } \ " ({queuedMedals.Count} to display)" ) ;
2024-11-28 18:19:00 +08:00
Schedule ( ( ) = > LoadComponentAsync ( medalAnimation , m = >
2024-11-26 16:58:50 +08:00
{
queuedMedals . Enqueue ( m ) ;
showNextMedal ( ) ;
2024-11-28 18:19:00 +08:00
} ) ) ;
2024-11-26 13:13:07 +08:00
}
protected override bool OnClick ( ClickEvent e )
{
2024-11-26 13:42:30 +08:00
progressDisplayByUser ( ) ;
2024-11-26 13:13:07 +08:00
return true ;
}
public override bool OnPressed ( KeyBindingPressEvent < GlobalAction > e )
{
if ( e . Action = = GlobalAction . Back )
{
2024-11-26 13:42:30 +08:00
progressDisplayByUser ( ) ;
2024-11-26 13:13:07 +08:00
return true ;
}
return base . OnPressed ( e ) ;
2024-02-20 19:53:46 +08:00
}
2024-11-26 13:42:30 +08:00
private void progressDisplayByUser ( )
2024-02-20 19:53:46 +08:00
{
2024-11-26 13:45:40 +08:00
// Dismissing may sometimes play out the medal animation rather than immediately dismissing.
if ( currentMedalDisplay ? . Dismiss ( ) = = false )
return ;
2024-11-26 13:13:07 +08:00
currentMedalDisplay = null ;
2024-11-26 13:42:30 +08:00
showNextMedal ( ) ;
2024-11-26 13:13:07 +08:00
}
2024-02-20 19:53:46 +08:00
2024-11-26 16:58:50 +08:00
private void showNextMedal ( )
2024-11-26 13:13:07 +08:00
{
2024-11-26 16:58:50 +08:00
// If already displayed, keep displaying medals regardless of activation mode changes.
if ( OverlayActivationMode . Value ! = OverlayActivation . All & & State . Value = = Visibility . Hidden )
2024-02-20 20:08:02 +08:00
return ;
2024-11-26 16:58:50 +08:00
// A medal is already displaying.
2024-11-26 13:13:07 +08:00
if ( currentMedalDisplay ! = null )
2024-02-20 20:44:25 +08:00
return ;
2024-11-26 13:42:30 +08:00
if ( queuedMedals . TryDequeue ( out currentMedalDisplay ) )
2024-11-26 13:13:07 +08:00
{
2024-11-26 16:58:50 +08:00
Logger . Log ( $"Displaying \" { currentMedalDisplay . Medal . Name } \ "" ) ;
medalContainer . Add ( currentMedalDisplay ) ;
2024-11-26 13:42:30 +08:00
Show ( ) ;
2024-02-20 19:53:46 +08:00
}
2024-11-26 17:00:32 +08:00
else if ( State . Value = = Visibility . Visible )
{
Logger . Log ( "All queued medals have been displayed, hiding overlay!" ) ;
base . Hide ( ) ;
}
2024-02-20 19:53:46 +08:00
}
protected override void Dispose ( bool isDisposing )
{
2024-11-28 17:24:56 +08:00
// this event subscription fires async loads, which hard-fail if `CompositeDrawable.disposalCancellationSource` is canceled, which happens in the base call.
// therefore, unsubscribe from this event early to reduce the chances of a stray event firing at an inconvenient spot.
2024-02-20 19:53:46 +08:00
if ( api . IsNotNull ( ) )
api . NotificationsClient . MessageReceived - = handleMedalMessages ;
2024-11-28 17:24:56 +08:00
base . Dispose ( isDisposing ) ;
2024-02-20 19:53:46 +08:00
}
}
}