mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 10:12:54 +08:00
Merge pull request #26183 from peppy/menu-supporter-display
Show supporter love message at main menu and remove the disclaimer screen
This commit is contained in:
commit
f5a3124e20
@ -1,19 +1,27 @@
|
||||
// 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.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Screens.Menu;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
public partial class TestSceneDisclaimer : ScreenTestScene
|
||||
public partial class TestSceneSupporterDisplay : OsuTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
AddStep("load disclaimer", () => LoadScreen(new Disclaimer()));
|
||||
AddStep("create display", () =>
|
||||
{
|
||||
Child = new SupporterDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("toggle support", () =>
|
||||
{
|
@ -21,8 +21,6 @@ namespace osu.Game.Screens
|
||||
{
|
||||
public partial class Loader : StartupScreen
|
||||
{
|
||||
private bool showDisclaimer;
|
||||
|
||||
public Loader()
|
||||
{
|
||||
ValidForResume = false;
|
||||
@ -35,13 +33,7 @@ namespace osu.Game.Screens
|
||||
private LoadingSpinner spinner;
|
||||
private ScheduledDelegate spinnerShow;
|
||||
|
||||
protected virtual OsuScreen CreateLoadableScreen()
|
||||
{
|
||||
if (showDisclaimer)
|
||||
return new Disclaimer(getIntroSequence());
|
||||
|
||||
return getIntroSequence();
|
||||
}
|
||||
protected virtual OsuScreen CreateLoadableScreen() => getIntroSequence();
|
||||
|
||||
private IntroScreen getIntroSequence()
|
||||
{
|
||||
@ -107,9 +99,8 @@ namespace osu.Game.Screens
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game, OsuConfigManager config)
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
showDisclaimer = game.IsDeployedBuild;
|
||||
introSequence = config.Get<IntroSequence>(OsuSetting.IntroSequence);
|
||||
}
|
||||
|
||||
|
@ -1,227 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public partial class Disclaimer : StartupScreen
|
||||
{
|
||||
private SpriteIcon icon;
|
||||
private Color4 iconColour;
|
||||
private LinkFlowContainer textFlow;
|
||||
private LinkFlowContainer supportFlow;
|
||||
|
||||
private Drawable heart;
|
||||
|
||||
private const float icon_y = -85;
|
||||
private const float icon_size = 30;
|
||||
|
||||
private readonly OsuScreen nextScreen;
|
||||
|
||||
private readonly Bindable<APIUser> currentUser = new Bindable<APIUser>();
|
||||
private FillFlowContainer fill;
|
||||
|
||||
private readonly List<ITextPart> expendableText = new List<ITextPart>();
|
||||
|
||||
public Disclaimer(OsuScreen nextScreen = null)
|
||||
{
|
||||
this.nextScreen = nextScreen;
|
||||
ValidForResume = false;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = OsuIcon.Logo,
|
||||
Size = new Vector2(icon_size),
|
||||
Y = icon_y,
|
||||
},
|
||||
fill = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Y = icon_y,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
textFlow = new LinkFlowContainer
|
||||
{
|
||||
Width = 680,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
TextAnchor = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Spacing = new Vector2(0, 2),
|
||||
},
|
||||
}
|
||||
},
|
||||
supportFlow = new LinkFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
TextAnchor = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Padding = new MarginPadding(20),
|
||||
Alpha = 0,
|
||||
Spacing = new Vector2(0, 2),
|
||||
},
|
||||
};
|
||||
|
||||
textFlow.AddText("this is osu!", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular));
|
||||
|
||||
expendableText.Add(textFlow.AddText("lazer", t =>
|
||||
{
|
||||
t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular);
|
||||
t.Colour = colours.PinkLight;
|
||||
}));
|
||||
|
||||
static void formatRegular(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular);
|
||||
static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold);
|
||||
|
||||
textFlow.NewParagraph();
|
||||
|
||||
textFlow.AddText("the next ", formatRegular);
|
||||
textFlow.AddText("major update", t =>
|
||||
{
|
||||
t.Font = t.Font.With(Typeface.Torus, 20, FontWeight.SemiBold);
|
||||
t.Colour = colours.Pink;
|
||||
});
|
||||
expendableText.Add(textFlow.AddText(" coming to osu!", formatRegular));
|
||||
textFlow.AddText(".", formatRegular);
|
||||
|
||||
textFlow.NewParagraph();
|
||||
textFlow.NewParagraph();
|
||||
|
||||
textFlow.NewParagraph();
|
||||
|
||||
iconColour = colours.Yellow;
|
||||
|
||||
// manually transfer the user once, but only do the final bind in LoadComplete to avoid thread woes (API scheduler could run while this screen is still loading).
|
||||
// the manual transfer is here to ensure all text content is loaded ahead of time as this is very early in the game load process and we want to avoid stutters.
|
||||
currentUser.Value = api.LocalUser.Value;
|
||||
currentUser.BindValueChanged(e =>
|
||||
{
|
||||
supportFlow.Children.ForEach(d => d.FadeOut().Expire());
|
||||
|
||||
if (e.NewValue.IsSupporter)
|
||||
{
|
||||
supportFlow.AddText("Eternal thanks to you for supporting osu!", formatSemiBold);
|
||||
}
|
||||
else
|
||||
{
|
||||
supportFlow.AddText("Consider becoming an ", formatSemiBold);
|
||||
supportFlow.AddLink("osu!supporter", "https://osu.ppy.sh/home/support", formatSemiBold);
|
||||
supportFlow.AddText(" to help support osu!'s development", formatSemiBold);
|
||||
}
|
||||
|
||||
supportFlow.AddIcon(FontAwesome.Solid.Heart, t =>
|
||||
{
|
||||
heart = t;
|
||||
|
||||
t.Padding = new MarginPadding { Left = 5, Top = 3 };
|
||||
t.Font = t.Font.With(size: 20);
|
||||
t.Origin = Anchor.Centre;
|
||||
t.Colour = colours.Pink;
|
||||
|
||||
Schedule(() => heart?.FlashColour(Color4.White, 750, Easing.OutQuint).Loop());
|
||||
});
|
||||
|
||||
if (supportFlow.IsPresent)
|
||||
supportFlow.FadeInFromZero(500);
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
if (nextScreen != null)
|
||||
LoadComponentAsync(nextScreen);
|
||||
|
||||
((IBindable<APIUser>)currentUser).BindTo(api.LocalUser);
|
||||
}
|
||||
|
||||
public override void OnSuspending(ScreenTransitionEvent e)
|
||||
{
|
||||
base.OnSuspending(e);
|
||||
|
||||
// Once this screen has finished being displayed, we don't want to unnecessarily handle user change events.
|
||||
currentUser.UnbindAll();
|
||||
}
|
||||
|
||||
public override void OnEntering(ScreenTransitionEvent e)
|
||||
{
|
||||
base.OnEntering(e);
|
||||
|
||||
icon.RotateTo(10);
|
||||
icon.FadeOut();
|
||||
icon.ScaleTo(0.5f);
|
||||
|
||||
icon.Delay(500).FadeIn(500).ScaleTo(1, 500, Easing.OutQuint);
|
||||
|
||||
using (BeginDelayedSequence(3000))
|
||||
{
|
||||
icon.FadeColour(iconColour, 200, Easing.OutQuint);
|
||||
icon.MoveToY(icon_y * 1.3f, 500, Easing.OutCirc)
|
||||
.RotateTo(-360, 520, Easing.OutQuint)
|
||||
.Then()
|
||||
.MoveToY(icon_y, 160, Easing.InQuart)
|
||||
.FadeColour(Color4.White, 160);
|
||||
|
||||
using (BeginDelayedSequence(520 + 160))
|
||||
{
|
||||
fill.MoveToOffset(new Vector2(0, 15), 160, Easing.OutQuart);
|
||||
Schedule(() => expendableText.SelectMany(t => t.Drawables).ForEach(t =>
|
||||
{
|
||||
t.FadeOut(100);
|
||||
t.ScaleTo(new Vector2(0, 1), 100, Easing.OutQuart);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
supportFlow.FadeOut().Delay(2000).FadeIn(500);
|
||||
double delay = 500;
|
||||
foreach (var c in textFlow.Children)
|
||||
c.FadeTo(0.001f).Delay(delay += 20).FadeIn(500);
|
||||
|
||||
this
|
||||
.FadeInFromZero(500)
|
||||
.Then(5500)
|
||||
.FadeOut(250)
|
||||
.ScaleTo(0.9f, 250, Easing.InQuint)
|
||||
.Finally(_ =>
|
||||
{
|
||||
if (nextScreen != null)
|
||||
this.Push(nextScreen);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -96,6 +96,7 @@ namespace osu.Game.Screens.Menu
|
||||
private Container logoTarget;
|
||||
private MenuTip menuTip;
|
||||
private FillFlowContainer bottomElementsFlow;
|
||||
private SupporterDisplay supporterDisplay;
|
||||
|
||||
private Sample reappearSampleSwoosh;
|
||||
|
||||
@ -179,6 +180,12 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
}
|
||||
},
|
||||
supporterDisplay = new SupporterDisplay
|
||||
{
|
||||
Margin = new MarginPadding(5),
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
},
|
||||
holdToExitGameOverlay?.CreateProxy() ?? Empty()
|
||||
});
|
||||
|
||||
@ -339,6 +346,9 @@ namespace osu.Game.Screens.Menu
|
||||
bottomElementsFlow
|
||||
.ScaleTo(0.9f, 1000, Easing.OutQuint)
|
||||
.FadeOut(500, Easing.OutQuint);
|
||||
|
||||
supporterDisplay
|
||||
.FadeOut(500, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public override void OnResuming(ScreenTransitionEvent e)
|
||||
@ -403,6 +413,9 @@ namespace osu.Game.Screens.Menu
|
||||
bottomElementsFlow
|
||||
.FadeOut(500, Easing.OutQuint);
|
||||
|
||||
supporterDisplay
|
||||
.FadeOut(500, Easing.OutQuint);
|
||||
|
||||
return base.OnExiting(e);
|
||||
}
|
||||
|
||||
|
167
osu.Game/Screens/Menu/SupporterDisplay.cs
Normal file
167
osu.Game/Screens/Menu/SupporterDisplay.cs
Normal file
@ -0,0 +1,167 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public partial class SupporterDisplay : CompositeDrawable
|
||||
{
|
||||
private LinkFlowContainer supportFlow = null!;
|
||||
|
||||
private Drawable heart = null!;
|
||||
|
||||
private readonly IBindable<APIUser> currentUser = new Bindable<APIUser>();
|
||||
|
||||
private Box backgroundBox = null!;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Height = 40;
|
||||
|
||||
AutoSizeAxes = Axes.X;
|
||||
AutoSizeDuration = 1000;
|
||||
AutoSizeEasing = Easing.OutQuint;
|
||||
|
||||
Masking = true;
|
||||
CornerExponent = 2.5f;
|
||||
CornerRadius = 15;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
backgroundBox = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.4f,
|
||||
},
|
||||
supportFlow = new LinkFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(10),
|
||||
Spacing = new Vector2(0, 2),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
const float font_size = 14;
|
||||
|
||||
static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: font_size, weight: FontWeight.SemiBold);
|
||||
|
||||
currentUser.BindTo(api.LocalUser);
|
||||
currentUser.BindValueChanged(e =>
|
||||
{
|
||||
supportFlow.Children.ForEach(d => d.FadeOut().Expire());
|
||||
|
||||
if (e.NewValue.IsSupporter)
|
||||
{
|
||||
supportFlow.AddText("Eternal thanks to you for supporting osu!", formatSemiBold);
|
||||
|
||||
backgroundBox.FadeColour(colours.Pink, 250);
|
||||
}
|
||||
else
|
||||
{
|
||||
supportFlow.AddText("Consider becoming an ", formatSemiBold);
|
||||
supportFlow.AddLink("osu!supporter", "https://osu.ppy.sh/home/support", formatSemiBold);
|
||||
supportFlow.AddText(" to help support osu!'s development", formatSemiBold);
|
||||
|
||||
backgroundBox.FadeColour(colours.Pink4, 250);
|
||||
}
|
||||
|
||||
supportFlow.AddIcon(FontAwesome.Solid.Heart, t =>
|
||||
{
|
||||
heart = t;
|
||||
|
||||
t.Padding = new MarginPadding { Left = 5, Top = 1 };
|
||||
t.Font = t.Font.With(size: font_size);
|
||||
t.Origin = Anchor.Centre;
|
||||
t.Colour = colours.Pink;
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
heart?.FlashColour(Color4.White, 750, Easing.OutQuint).Loop();
|
||||
});
|
||||
});
|
||||
}, true);
|
||||
|
||||
this
|
||||
.FadeOut()
|
||||
.Delay(1000)
|
||||
.FadeInFromZero(800, Easing.OutQuint);
|
||||
|
||||
scheduleDismissal();
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
dismissalDelegate?.Cancel();
|
||||
|
||||
supportFlow.BypassAutoSizeAxes = Axes.X;
|
||||
this.FadeOut(500, Easing.OutQuint);
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
backgroundBox.FadeTo(0.6f, 500, Easing.OutQuint);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
backgroundBox.FadeTo(0.4f, 500, Easing.OutQuint);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
private ScheduledDelegate? dismissalDelegate;
|
||||
|
||||
private void scheduleDismissal()
|
||||
{
|
||||
dismissalDelegate?.Cancel();
|
||||
dismissalDelegate = Scheduler.AddDelayed(() =>
|
||||
{
|
||||
// If the user is hovering they may want to interact with the link.
|
||||
// Give them more time.
|
||||
if (IsHovered)
|
||||
{
|
||||
scheduleDismissal();
|
||||
return;
|
||||
}
|
||||
|
||||
dismissalDelegate?.Cancel();
|
||||
|
||||
AutoSizeEasing = Easing.In;
|
||||
supportFlow.BypassAutoSizeAxes = Axes.X;
|
||||
this
|
||||
.Delay(200)
|
||||
.FadeOut(750, Easing.Out);
|
||||
}, 6000);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user